前端进度条性能优化、仿真结束后后处理优化;后端C代码生成流程优化:先识别来源,再按照已知未知量需求排序,最后局部求解
This commit is contained in:
1 parent
0dcb465d84
commit
91bd9fb252
77 files changed
+11223
-551
No files matched your search
@@ -0,0 +1,152 @@
|
||||
"""Compile-time variable supply contracts, separate from physical flow direction.
|
||||
|
||||
Equation ports may participate in a simultaneous solve. Fixed ports (for example
|
||||
an Amesim node's reference/branch ports) require complementary local supplies.
|
||||
These declarations do not add numerical state or Python evaluation callbacks.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Literal, Mapping
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .ports import PortDefinition
|
||||
|
||||
|
||||
VARIABLE_LABELS = {
|
||||
"p": "压力", "T": "温度", "m_flow": "质量流率", "H_flow": "能量流率",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PortComputation:
|
||||
inputs: tuple[str, ...] = ()
|
||||
outputs: tuple[str, ...] = ()
|
||||
mode: Literal["equation", "fixed"] = "equation"
|
||||
# Output p/T aliases an input on another port of the same component.
|
||||
reference_port: str | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.mode not in {"equation", "fixed"}:
|
||||
raise ValueError(f"Unknown port computation mode: {self.mode}")
|
||||
members = (*self.inputs, *self.outputs)
|
||||
if len(set(members)) != len(members) or set(members) - VARIABLE_LABELS.keys():
|
||||
raise ValueError("Port computation variables must be unique, supported quantities.")
|
||||
if self.reference_port and not {"p", "T"}.issubset(self.outputs):
|
||||
raise ValueError("A reference alias must supply pressure and temperature.")
|
||||
|
||||
def as_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"mode": self.mode,
|
||||
"inputs": list(self.inputs),
|
||||
"outputs": list(self.outputs),
|
||||
**({"referencePort": self.reference_port} if self.reference_port else {}),
|
||||
}
|
||||
|
||||
|
||||
THERMODYNAMIC_SUPPLY = PortComputation(inputs=("m_flow", "H_flow"), outputs=("p", "T"))
|
||||
FLOW_SUPPLY = PortComputation(inputs=("p", "T"), outputs=("m_flow", "H_flow"))
|
||||
ZERO_FLOW_SUPPLY = PortComputation(outputs=("m_flow", "H_flow"))
|
||||
IMPLICIT_PNEUMATIC = PortComputation()
|
||||
NODE_REFERENCE = PortComputation(inputs=("p", "T"), outputs=("m_flow", "H_flow"), mode="fixed")
|
||||
NODE_BRANCH = PortComputation(inputs=("m_flow", "H_flow"), outputs=("p", "T"),
|
||||
mode="fixed", reference_port="port_2")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PortSupplyIssue:
|
||||
code: str
|
||||
message: str
|
||||
endpoint: tuple[str, str] | None = None
|
||||
|
||||
|
||||
class PortSupplyError(ValueError):
|
||||
def __init__(self, issue: PortSupplyIssue):
|
||||
self.issue = issue
|
||||
super().__init__(f"{issue.code}: {issue.message}")
|
||||
|
||||
|
||||
def port_supply_issue(first: PortDefinition, second: PortDefinition,
|
||||
first_label: str | None = None, second_label: str | None = None
|
||||
) -> PortSupplyIssue | None:
|
||||
"""Check fixed causality; ordinary equation-to-equation links stay legal."""
|
||||
if first.kind != second.kind or first.domain != second.domain:
|
||||
return None # Domain/type checks own their existing, more specific errors.
|
||||
a, b = first.computation, second.computation
|
||||
if not any(item and item.mode == "fixed" for item in (a, b)):
|
||||
return None
|
||||
for consumer, supplier, consumer_label, supplier_label in (
|
||||
(a, b, first_label or first.name, second_label or second.name),
|
||||
(b, a, second_label or second.name, first_label or first.name),
|
||||
):
|
||||
if consumer is None:
|
||||
continue
|
||||
missing = [name for name in consumer.inputs
|
||||
if supplier is None or name not in supplier.outputs]
|
||||
if missing:
|
||||
quantities = "、".join(VARIABLE_LABELS[name] for name in missing)
|
||||
return PortSupplyIssue(
|
||||
"CONNECTION_VARIABLE_SUPPLY_MISSING",
|
||||
f"{consumer_label} 需要对端提供{quantities},但 {supplier_label} 未提供;"
|
||||
"请检查参考口与支路口的连接。气体流向反转不会改变这一供需关系。",
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def reference_supply_issues(
|
||||
ports: Mapping[tuple[str, str], PortDefinition],
|
||||
adjacency: Mapping[tuple[str, str], tuple[str, str]],
|
||||
) -> list[PortSupplyIssue]:
|
||||
"""Follow declared aliases to reject a reference ring without an origin.
|
||||
|
||||
This is a supply check, not a whole-system execution scheduler. Reference
|
||||
chains are iterative to support deep networks without Python recursion.
|
||||
"""
|
||||
issues = []
|
||||
resolved: dict[str, set[tuple[str, str]]] = {'p': set(), 'T': set()}
|
||||
for endpoint, port in ports.items():
|
||||
contract = port.computation
|
||||
if not contract or contract.mode != "fixed" or not {"p", "T"}.issubset(contract.inputs):
|
||||
continue
|
||||
if endpoint not in adjacency:
|
||||
continue # Existing unconnected-port checks handle incomplete drawings.
|
||||
for variable in ("p", "T"):
|
||||
current = endpoint
|
||||
visited: set[tuple[str, str]] = set()
|
||||
chain: list[str] = []
|
||||
while True:
|
||||
if current in resolved[variable]:
|
||||
resolved[variable].update(visited)
|
||||
break
|
||||
if current in visited:
|
||||
issues.append(PortSupplyIssue(
|
||||
"REFERENCE_SUPPLY_CYCLE",
|
||||
f"{'.'.join(endpoint)} 的{VARIABLE_LABELS[variable]}参考形成循环,"
|
||||
f"没有实际提供者:{' → '.join(chain)} → {'.'.join(current)}。",
|
||||
endpoint,
|
||||
))
|
||||
break
|
||||
visited.add(current)
|
||||
chain.append('.'.join(current))
|
||||
supplier = adjacency.get(current)
|
||||
if supplier is None:
|
||||
issues.append(PortSupplyIssue(
|
||||
"REFERENCE_SUPPLY_UNCONNECTED",
|
||||
f"{'.'.join(endpoint)} 的{VARIABLE_LABELS[variable]}参考链在 "
|
||||
f"{'.'.join(current)} 中断:该参考输入尚未连接。", endpoint,
|
||||
))
|
||||
break
|
||||
supplied = ports.get(supplier)
|
||||
supply = supplied.computation if supplied else None
|
||||
if supply is None or variable not in supply.outputs:
|
||||
# Direct errors are already reported per connection. An
|
||||
# indirect failure is explained at that failing connection.
|
||||
break
|
||||
if supply.reference_port is None:
|
||||
resolved[variable].update(visited)
|
||||
break
|
||||
chain.append('.'.join(supplier))
|
||||
current = supplier[0], supply.reference_port
|
||||
if current not in ports:
|
||||
raise ValueError(f"Invalid reference port declaration: {current}")
|
||||
return issues
|
||||
@@ -3,6 +3,8 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Literal
|
||||
|
||||
from .port_computation import IMPLICIT_PNEUMATIC, PortComputation
|
||||
|
||||
|
||||
PortKind = Literal["physical", "signal"]
|
||||
PortNominalRole = Literal["inlet", "outlet", "bidirectional", "input", "output"]
|
||||
@@ -45,6 +47,7 @@ class PortDefinition:
|
||||
nominal_role: PortNominalRole
|
||||
positive_flow_direction: Literal["intoComponent"] | None = None
|
||||
variables: tuple[PortVariableDefinition, ...] = ()
|
||||
computation: PortComputation | None = None
|
||||
|
||||
@classmethod
|
||||
def pneumatic(
|
||||
@@ -52,6 +55,7 @@ class PortDefinition:
|
||||
name: str,
|
||||
*,
|
||||
nominal_role: Literal["inlet", "outlet", "bidirectional"] = "bidirectional",
|
||||
computation: PortComputation = IMPLICIT_PNEUMATIC,
|
||||
) -> PortDefinition:
|
||||
return cls(
|
||||
name=name,
|
||||
@@ -59,6 +63,7 @@ class PortDefinition:
|
||||
domain="pneumatic",
|
||||
nominal_role=nominal_role,
|
||||
positive_flow_direction="intoComponent",
|
||||
computation=computation,
|
||||
variables=(
|
||||
PortVariableDefinition(
|
||||
"p",
|
||||
@@ -188,6 +193,7 @@ class PortDefinition:
|
||||
"nominalRole": self.nominal_role,
|
||||
"positiveFlowDirection": self.positive_flow_direction,
|
||||
"variables": [variable.as_interface_dict() for variable in self.variables],
|
||||
**({"computation": self.computation.as_dict()} if self.computation else {}),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user