80 lines
6.8 KiB
Python
80 lines
6.8 KiB
Python
"""Component parameters, ports and output definitions; numerical equations execute in C."""
|
|
from __future__ import annotations
|
|
from collections.abc import Mapping
|
|
from app.simulation.core.base import AlgebraicComponent
|
|
from app.simulation.core.catalog import ComponentDisplaySpec, ParameterGroupDisplaySpec, PortDisplaySpec
|
|
from app.simulation.core.metadata import ParameterCondition, ParameterDefinition, ParameterOption, ResultVariableDefinition
|
|
from app.simulation.core.medium import IdealGasMedium
|
|
from app.simulation.core.ports import PortDefinition
|
|
def _ud00_stage_parameters(index: int) -> tuple[ParameterDefinition, ...]:
|
|
visible_when = () if index == 1 else (ParameterCondition('nstages', tuple((float(stage_count) for stage_count in range(index, 9)))),)
|
|
return (ParameterDefinition(f'start{index}', 0.0 if index == 1 else 1.0, label=f'第 {index} 段起点', quantity='dimensionless', unit='', description=f'第 {index} 段开始时的输出值。', visible_when=visible_when), ParameterDefinition(f'end{index}', 1.0, label=f'第 {index} 段终点', quantity='dimensionless', unit='', description=f'第 {index} 段结束时的输出值。', visible_when=visible_when), ParameterDefinition(f't{index}', 1.0 if index == 1 else 0.0, label=f'第 {index} 段时长', quantity='time', unit='s', minimum=0.0, description=f'第 {index} 段的持续时间。', visible_when=visible_when))
|
|
|
|
_UD00_STAGE_PARAMETERS = tuple((parameter for stage_index in range(1, 9) for parameter in _ud00_stage_parameters(stage_index)))
|
|
|
|
class AmesimStep0(AlgebraicComponent):
|
|
"""AMESim STEP0 scalar step signal source."""
|
|
MODEL_TYPE = 'amesim_step0'
|
|
MODEL_VERSION = '0.1.0'
|
|
PORTS = (PortDefinition.signal('out', nominal_role='output'),)
|
|
PARAMETERS = (ParameterDefinition('initial', 0.0, label='初始值', quantity='dimensionless', unit=''), ParameterDefinition('final', 1.0, label='阶跃后值', quantity='dimensionless', unit=''), ParameterDefinition('time', 0.0, label='阶跃时间', quantity='time', unit='s'))
|
|
RESULT_VARIABLES = (ResultVariableDefinition('y', '输出', 'dimensionless', '', 'signal', 10),)
|
|
DISPLAY = ComponentDisplaySpec(label='STEP0 阶跃信号', library_id='amesim', category_id='signals', symbol='amesim_step0', ports=(PortDisplaySpec('out', 'right', order=10),), order=10)
|
|
|
|
def __init__(self, name: str, medium: IdealGasMedium, *, initial: float=0.0, final: float=1.0, time: float=0.0) -> None:
|
|
super().__init__(name=name)
|
|
self.set_parameter_values({'initial': initial, 'final': final, 'time': time})
|
|
self.initial = float(initial)
|
|
self.final = float(final)
|
|
self.time = float(time)
|
|
self.out = self.register_declared_port('out')
|
|
|
|
@classmethod
|
|
def create(cls, *, name: str, medium: IdealGasMedium, parameters: Mapping[str, float]) -> 'AmesimStep0':
|
|
return cls(name=name, medium=medium, initial=parameters['initial'], final=parameters['final'], time=parameters['time'])
|
|
EQUATIONS = ()
|
|
|
|
class AmesimUd00(AlgebraicComponent):
|
|
"""AMESim UD00 piecewise-linear scalar signal source."""
|
|
MODEL_TYPE = 'amesim_ud00'
|
|
MODEL_VERSION = '0.2.0'
|
|
PORTS = (PortDefinition.signal('out', nominal_role='output'),)
|
|
PARAMETERS = (ParameterDefinition('tstart', 0.0, label='启动时间', quantity='time', unit='s', description='分段信号开始输出第一段之前的等待时间。'), *_UD00_STAGE_PARAMETERS, ParameterDefinition('nstages', 1.0, label='段数', quantity='dimensionless', unit='', minimum=1.0, maximum=8.0, editor='choice', options=tuple((ParameterOption(float(stage_count), str(stage_count)) for stage_count in range(1, 9))), description='参与输出计算的有效线性分段数量。'), ParameterDefinition('iscyclic', 0.0, label='循环', quantity='dimensionless', unit='', minimum=0.0, maximum=1.0, editor='choice', options=(ParameterOption(0.0, '否'), ParameterOption(1.0, '是')), description='当前公共协议编码:0 表示单次输出,1 表示循环输出。'))
|
|
RESULT_VARIABLES = (ResultVariableDefinition('y', '输出', 'dimensionless', '', 'signal', 10),)
|
|
DISPLAY = ComponentDisplaySpec(label='UD00 分段线性信号', library_id='amesim', category_id='signals', symbol='amesim_ud00', ports=(PortDisplaySpec('out', 'right', order=10),), order=20, parameter_groups=(ParameterGroupDisplaySpec(id='stages', label='分段参数', parameters=tuple((parameter.name for parameter in _UD00_STAGE_PARAMETERS)), order=10),))
|
|
|
|
def __init__(self, name: str, medium: IdealGasMedium, *, tstart: float=0.0, starts: tuple[float, ...]=(0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0), ends: tuple[float, ...]=(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0), durations: tuple[float, ...]=(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), nstages: int=1, iscyclic: bool=False) -> None:
|
|
super().__init__(name=name)
|
|
if len(starts) != 8 or len(ends) != 8 or len(durations) != 8:
|
|
raise ValueError('UD00 requires exactly eight start, end, and duration values.')
|
|
if nstages < 1 or nstages > 8:
|
|
raise ValueError('UD00 nstages must be between 1 and 8.')
|
|
self.tstart = float(tstart)
|
|
self.starts = tuple((float(value) for value in starts))
|
|
self.ends = tuple((float(value) for value in ends))
|
|
self.durations = tuple((float(value) for value in durations))
|
|
self.nstages = int(nstages)
|
|
self.iscyclic = bool(iscyclic)
|
|
values: dict[str, float] = {'tstart': self.tstart, 'nstages': float(self.nstages), 'iscyclic': float(int(self.iscyclic))}
|
|
for index in range(1, 9):
|
|
values[f'start{index}'] = self.starts[index - 1]
|
|
values[f'end{index}'] = self.ends[index - 1]
|
|
values[f't{index}'] = self.durations[index - 1]
|
|
self.set_parameter_values(values)
|
|
self.out = self.register_declared_port('out')
|
|
|
|
@classmethod
|
|
def create(cls, *, name: str, medium: IdealGasMedium, parameters: Mapping[str, float]) -> 'AmesimUd00':
|
|
nstages = parameters['nstages']
|
|
iscyclic = parameters['iscyclic']
|
|
definitions = {definition.name: definition for definition in cls.PARAMETERS}
|
|
for parameter_name, value in (('nstages', nstages), ('iscyclic', iscyclic)):
|
|
numeric_value = float(value)
|
|
if not numeric_value.is_integer():
|
|
raise ValueError(f'UD00 {parameter_name} must be an integer.')
|
|
message = definitions[parameter_name].validation_message(numeric_value)
|
|
if message is not None:
|
|
raise ValueError(f'UD00 {parameter_name} {message}.')
|
|
return cls(name=name, medium=medium, tstart=parameters['tstart'], starts=tuple((parameters[f'start{index}'] for index in range(1, 9))), ends=tuple((parameters[f'end{index}'] for index in range(1, 9))), durations=tuple((parameters[f't{index}'] for index in range(1, 9))), nstages=int(nstages), iscyclic=bool(int(iscyclic)))
|
|
EQUATIONS = ()
|