Source code for code_aster.Solvers.Basics.context

# coding=utf-8
# --------------------------------------------------------------------
# Copyright (C) 1991 - 2026 - EDF - www.code-aster.org
# This file is part of code_aster.
#
# code_aster is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# code_aster is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with code_aster.  If not, see <http://www.gnu.org/licenses/>.
# --------------------------------------------------------------------

"""
Base objects used to solve generic non linear problems.
"""

from functools import wraps

from ...Cata.Language.SyntaxObjects import _F
from ...Utilities import logger, no_new_attributes
from .bases import ProblemType as PBT


[docs]class Context: """Object that stores the objects required by a :py:class:`NonLinearOperator`. It only stores objects that are shared at different levels of the algorithm. Objects only used by one stage/object should be created there. If different objects should be created at each timestep for example, they should not be here. Attributes: problem: :py:class:`PhysicalProblem` object state: :py:class:`PhysicalState` object result: :py:class:`Result` object (:py:class:`NonLinearResult`, :py:class:`ThermalResult` , :py:class:`DryingResult`) problem_type: :py:class:`ProblemType` enum value keywords: Part of the user keywords oper: :py:class:`BaseOperators` object stepper: :py:class:`TimeStepper` object contact: :py:class:`ContactManager` object linear_solver: :py:class:`LinearSolver` object """ # FIXME: with TimeScheme.Multiple? several '.oper'? one '.oper' per StepSolver? # FIXME: Creating all objects in ops should allow overloading # FIXME: and to known syntax in one place (for instance: RECH_LINEAIRE in NewtonSolver)
[docs] class KeywordsStore: """Container that stores and gives access to some user keywords.""" _dict_kwds = None __setattr__ = no_new_attributes(object.__setattr__)
[docs] def __init__(self, keywords): self._dict_kwds = keywords
[docs] def __getitem__(self, keyword): """Return a keyword value. Args: keyword (str): Simple keyword. Returns: *misc*: Keyword value. """ return self._dict_kwds[keyword]
[docs] def get(self, keyword, parameter=None, default=None): """Return a keyword value. Args: keyword (str): Simple or factor keyword. parameter (str|None): Simple keyword under the factor keyword, or *None*. default (*misc*): Default value if the keyword is undefined. Returns: *misc*: Keyword value. """ kwds = self._dict_kwds if parameter is not None: if kwds.get(keyword) is None: return default return _F(kwds[keyword])[0].get(parameter, default) return kwds.get(keyword, default)
_problem = _type = _state = _keywords = _result = None _stepper = _oper = _contact = _linsolv = None __setattr__ = no_new_attributes(object.__setattr__)
[docs] def __init__(self): self._type = None self._keywords = Context.KeywordsStore({}) self._problem = None self._state = None self._result = None self._stepper = None self._oper = None self._contact = None self._linsolv = None
[docs] def check(self): """Check for required/optional attributes.""" for attr in ("problem_type", "problem", "state", "result", "oper", "keywords"): if not getattr(self, attr): logger.error(f"{attr!r} attribute is required") for attr in ("stepper", "linear_solver"): if not getattr(self, attr): logger.warning(f"{attr!r} is not yet defined")
@property def problem_type(self): """ProblemType: Attribute that holds the type of problem.""" return self._type @problem_type.setter def problem_type(self, value): assert self._type is None, "must be set only once!" self._type = value @property def keywords(self): """Dict: Attribute that holds the keywords object.""" return self._keywords @keywords.setter def keywords(self, value_dict): assert isinstance(value_dict, dict), f"unsupported type: {type(value_dict)}" self._keywords = Context.KeywordsStore(value_dict)
[docs] def get_keyword(self, keyword, parameter=None, default=None): """ "Return a keyword value. Args: keyword (str): Simple or factor keyword. parameter (str|None): Simple keyword under the factor keyword, or *None*. default (*misc*): Default value if the keyword is undefined. Returns: *misc*: Keyword value. """ return self._keywords.get(keyword, parameter, default)
@property def problem(self): """PhysicalProblem: current problem description.""" return self._problem @problem.setter def problem(self, problem): assert self._problem is None, "must be set only once!" self._problem = problem @property def state(self): """PhysicalState: current state.""" return self._state @state.setter def state(self, state): assert self._state is None, "must be set only once!" self._state = state @property def result(self): """Result: Attribute that holds the result object.""" return self._result @result.setter def result(self, value): self._result = value @property def stepper(self): """TimeStepper: Attribute that holds the time stepper.""" return self._stepper @stepper.setter def stepper(self, value): self._stepper = value @property def oper(self): """Operators: Object that adapts operators for each type of problem.""" return self._oper @oper.setter def oper(self, value): self._oper = value @property def contact(self): """ContactManager: Object to solve contact conditions""" return self._contact @contact.setter def contact(self, value): self._contact = value @property def linear_solver(self): """LinearSolver: Attribute that holds the linear solver.""" return self._linsolv @linear_solver.setter def linear_solver(self, value): self._linsolv = value
[docs]def check_access(alt=None): """Decorator to wrap TestCase methods by calling writeResult""" def decorator(method): @wraps(method) def wrapper(inst, *args, **kwds): """wrapper""" required = alt or method.__name__ if required != "context" and required not in inst.__needs__: raise AttributeError(f"undeclared access to {required!r}") # logger.warning(f"undeclared access from {inst.__class__.__name__} to {required}") return method(inst, *args, **kwds) return wrapper return decorator
[docs]class ContextMixin: """Mixin object that wraps access to the objects of Context. Attributes: problem: :py:class:`PhysicalProblem` object state: :py:class:`PhysicalState` object result: :py:class:`Result` object (:py:class:`NonLinearResult`, :py:class:`ThermalResult`, :py:class:`DryingResult`) problem_type: :py:class:`ProblemType` enum value keywords: Part of the user keywords oper: :py:class:`BaseOperators` object contact: :py:class:`ContactManager` object linear_solver: :py:class:`LinearSolver` object """ # each class must declared what attributes it needs to access __needs__ = () _ctxt = None __setattr__ = no_new_attributes(object.__setattr__)
[docs] @classmethod def builder(cls, context): """Default builder for :py:class:`ContextMixin` object. Should be subclassed for non trivial constructor. Args: context (Context): Context of the problem. Returns: instance: New object. """ instance = cls() instance.context = context return instance
[docs] def __init__(self): super().__init__() self._ctxt = Context()
@property @check_access() def context(self): """Data: Context attached to the object.""" return self._ctxt @context.setter @check_access() def context(self, other): assert isinstance(other, Context) self._ctxt = other # convenient shortcuts properties @property @check_access() def problem_type(self): """ProblemType: Attribute that holds the type of problem.""" return self._ctxt.problem_type @problem_type.setter @check_access() def problem_type(self, value): self._ctxt.problem_type = value @property @check_access() def keywords(self): """Dict: Attribute that holds the keywords object.""" return self._ctxt.keywords @keywords.setter @check_access() def keywords(self, value_dict): self._ctxt.keywords = value_dict
[docs] @check_access(alt="keywords") def get_keyword(self, keyword, parameter=None, default=None): """ "Return a keyword value. Args: keyword (str): Simple or factor keyword. parameter (str|None): Simple keyword under the factor keyword, or *None*. default (*misc*): Default value if the keyword is undefined. Returns: *misc*: Keyword value. """ return self._ctxt.get_keyword(keyword, parameter, default)
@property @check_access() def problem(self): """PhysicalProblem: current problem description.""" return self._ctxt.problem @problem.setter @check_access() def problem(self, value): self._ctxt.problem = value @property @check_access() def state(self): """PhysicalState: current state.""" return self._ctxt.state @state.setter @check_access() def state(self, value): self._ctxt.state = value @property @check_access() def result(self): """Result: Attribute that holds the result object.""" return self._ctxt.result @result.setter @check_access() def result(self, value): self._ctxt.result = value @property @check_access() def stepper(self): """TimeStepper: Attribute that holds the time stepper.""" return self._ctxt.stepper @stepper.setter @check_access() def stepper(self, value): self._ctxt.stepper = value @property @check_access() def oper(self): """Operators: Object that adapts operators for each type of problem.""" return self._ctxt.oper @oper.setter @check_access() def oper(self, value): self._ctxt.oper = value @property @check_access() def contact(self): """ContactManager: Object to solve contact conditions""" return self._ctxt.contact @contact.setter @check_access() def contact(self, value): self._ctxt.contact = value @property @check_access() def linear_solver(self): """LinearSolver: Attribute that holds the linear solver.""" return self._ctxt.linear_solver @linear_solver.setter @check_access() def linear_solver(self, value): self._ctxt.linear_solver = value