Source code for g3d.calc.calculators.generic

"""Generic calculator class"""
import os
from typing import Union, Any, Tuple
from collections.abc import Callable

if os.environ.get('__USE_CUDA', '0') == '1':
    import cupy as np
    from cupy import ndarray, float64
else:
    import numpy as np
    from numpy import ndarray, float64

from g3d.defs import G3DCode
from g3d.defs import CodeType
from g3d.defs import G3DWorld

from g3d.gas import Gas

from g3d.defs import CFlowVariables
from g3d.defs import CFlowRegime
from g3d.defs import AngleType
from g3d.defs import CFlowAdditionMode
from .newton_raphson import G3DNewtonRaphsonSolver


[docs] class G3DGenericCalculator(G3DCode): """Generic calculator class (base class for all problem-specific calculators)""" def __init__(self, *args: Any, **kwargs: Any) -> None: """Initialize calculator parameters""" self._vars = CFlowVariables() self._gas = Gas() # default self.__default_mode = CFlowRegime.SUBSONIC self.__default_outdim = 1 self.__default_precision = 2 self.__default_angle_type = AngleType.RADIANS self.__default_round = False self.__default_add_mode = CFlowAdditionMode.ABSOLUTE # initialize member variables self._mode = self.__default_mode self._outdim = self.__default_outdim self._precision = self.__default_precision self._angle_type = self.__default_angle_type self.__round = self.__default_round self._add_mode = self.__default_add_mode super().__init__(*args, **kwargs) self._newton = G3DNewtonRaphsonSolver(update_code_type=False) self.__eval_is_reset = False def _set_code_type(self) -> None: super()._set_code_type() g3d_world = G3DWorld() g3d_world.info.code_type = CodeType.CALCULATOR g3d_world.info.code_name = 'cflow.py' g3d_world.info.code_description = 'a python-based compressible flow calculator' def _set_default(self) -> None: super()._set_default() self._tag = 'g3d_calculator' self.__reset_eval() @property def gas(self) -> Gas: """Gas object""" return self._gas @gas.setter def gas(self, g: Gas) -> None: self._gas = g self._associate() @property def newton(self) -> G3DNewtonRaphsonSolver: """Newton-Raphson solver""" return self._newton @newton.setter def newton(self, n: G3DNewtonRaphsonSolver) -> None: self._newton = n def _get_newton_kernel(self) -> G3DNewtonRaphsonSolver: newton = G3DNewtonRaphsonSolver(nrs=self._newton) return newton def _angle_import(self, *args: Any, **kwargs: Any) -> Union[tuple, int]: if len(args) < 1: return -1 angle_type = AngleType.UNDEFINED for key, value in kwargs.items(): if key == "angle_type": angle_type = value a = [] if self._angle_type is AngleType.RADIANS or angle_type is AngleType.RADIANS: for arg in args: a.append(arg) else: for arg in args: a.append(np.pi * arg / 180.) if len(a) == 1: return a[0] return tuple(a) def _angle_export(self, *args: Any, **kwargs: Any) -> Union[tuple, int]: if len(args) < 1: return -1 angle_type = AngleType.UNDEFINED for key, value in kwargs.items(): if key == "angle_type": angle_type = value a = [] if self._angle_type is AngleType.RADIANS or angle_type is AngleType.RADIANS: for arg in args: a.append(arg) else: for arg in args: a.append(180. * arg / np.pi) if len(a) == 1: return a[0] return tuple(a) def __reset_eval(self) -> None: self._mode = self.__default_mode self._outdim = self.__default_outdim self._precision = self.__default_precision self._angle_type = self.__default_angle_type self.__round = self.__default_round self._add_mode = self.__default_add_mode def _reset_eval(self, **kwargs: Any) -> None: if not self.__eval_is_reset: # reset eval parameters to default values self.__reset_eval() # override default values if len(kwargs) != 0: for key, value in kwargs.items(): if not self.set_from_key_value(key, value): print('warning! parameter %s is not defined' % key) self.__eval_is_reset = True def _nullify(self) -> None: self._vars.nullify()
[docs] def print(self) -> None: """Prints allocated variables""" self._vars.print()
[docs] def backup(self) -> None: """Backup of stored variables""" self._vars.backup()
[docs] def restore(self) -> None: """Restore variable state from backup""" self._vars.restore()
def _init(self, names: list, *args: Any, **kwargs: Any) -> None: self._vars.init(names) if len(args) != 0 or len(kwargs) != 0: self._vars.get_from_args(*args) if len(kwargs) != 0: for key, value in kwargs.items(): if not self.set_from_key_value(key, value): print('warning! parameter %s is not defined' % key) self._vars.arrayify() self.__eval_is_reset = False
[docs] def set_from_key_value(self, key: str, value: Any) -> bool: """Checks if provided key is in list of defined keys and updates calculator settings accordingly""" if super().set_from_key_value(key, value): return True elif self._vars.set_from_key_value(key, value): return True elif self._newton.set_from_key_value(key, value): return True elif self._gas.set_from_key_value(key, value): return True elif key == 'precision' or key == 'prec': self._precision = value self.__round = True return True elif key == 'angle_type': if type(value) is str: if value == 'radians' or value == 'rad': self._angle_type = AngleType.RADIANS elif value == 'degrees' or value == 'deg': self._angle_type = AngleType.DEGREES else: self._angle_type = value return True elif key == 'mode': if type(value) is str: if value == 'supersonic': self._mode = CFlowRegime.SUPERSONIC elif value == 'subsonic': self._mode = CFlowRegime.SUBSONIC elif value == 'weak': self._mode = CFlowRegime.WEAK elif value == 'strong': self._mode = CFlowRegime.STRONG else: self._mode = value return True elif key == 'add_mode': if type(value) is str: if value == 'absolute' or value == 'abs': self._add_mode = CFlowAdditionMode.ABSOLUTE if value == 'relative' or value == 'rel': self._add_mode = CFlowAdditionMode.RELATIVE if value == 'factor' or value == 'fact': self._add_mode = CFlowAdditionMode.FACTOR if value == 'percent' or value == 'per': self._add_mode = CFlowAdditionMode.PERCENT else: self._add_mode = value return True elif key == 'outdim': if value in [int, np.int64, np.int32]: self._outdim = value else: self._outdim = np.int32(value) return True return False
def _associate(self) -> None: pass def _eval(self, names: list, func: Callable[ [], Union[float, Tuple[float, ...], Tuple[float64, ...], ndarray]], *args: Any, **kwargs: Any) -> Union[Union[tuple[Any, ...], list[Any]], Any]: self._reset_eval() self._init(names, *args, **kwargs) truncate_result = self.__round outdim = self._outdim prec = self._precision ret = [] for i in range(outdim): ret.append(np.zeros(self._vars.size)) for i in range(self._vars.size): self._vars.get_from_array(i) out = func() if type(out) is not tuple: if type(out) is ndarray: if out.size == 1: out = float64(out) out = tuple([out]) if not truncate_result: for j in range(outdim): ret[j][i] = out[j] else: if type(prec) is not tuple: if type(prec) is list: if len(prec) == outdim: prec = tuple(prec) else: prec = prec[0] if type(prec) is not tuple: tmp = [] for j in range(outdim): tmp.append(prec) prec = tuple(tmp) for j in range(outdim): ret[j][i] = np.round(out[j], prec[j]) if self._vars.isscalar: for j in range(outdim): ret[j] = float64(np.squeeze(ret[j])) if outdim == 1: return ret[0] else: return tuple(ret) else: if outdim == 1: return ret[0] return ret
[docs] def reset_newton(self) -> None: """Resets Newton-Raphson solver to its default state""" self._newton.reset()
[docs] def newton_reset(self) -> None: """ .. deprecated:: 2.6 redirects to :func:`reset_newton` """ self.reset_newton()
[docs] def Newton_Raphson_solver(self, func: Callable[[float], float], *args: Any, **kwargs: Any) -> float: """Runs Newton-Raphson solver on specified function input: function (func), iteration start value (start_value), function target value (target_value) optional input (kwargs): numerical differentiation delta (delta, default 0.0001), maximum number of iterations (maxiter, default 100), tolerance (tol, default 1.e-6), lower function limit (lower_bound), upper function limit (upper_bound) Parameters ---------- func : Callable : function to solve *args : Any arguments (optional input): 1. start value 2. target value variable value(s) in above-listed order **kwargs : Any keyword-value pairs (optional input): valid keywords: 'delta', 'maxiter', 'tol', 'lower_bound', 'upper_bound' (any other keyword->argument input will be passed on to the specified function) Returns ------- float Newton-Raphson solver result Examples -------- Basic usage: $_>>> from g3d import cflow $_>>> result = cflow.Newton_Raphson_solver(func, start_value=10., target_value=0.) """ newton = self._get_newton_kernel() newton.set(*args, **kwargs) return newton.solve(func)