5 changed files with 475 additions and 763 deletions

No files matched your search

+42 -18
View File
@@ -11,15 +11,27 @@ from .components import (
) )
from .cycles import BraytonCycle from .cycles import BraytonCycle
from .mass_models import ( from .mass_models import (
apply_component_mass, PCHEGeometry,
calculate_component_mass, PCHEMaterial,
condenser_radiator_mass, PCHEDesignOptions,
has_default_mass_model, PCHE_STATE_UNIT_CONVENTION,
heater_reactor_mass, calculate_condenser_mass,
initialize_recuperator_segments, calculate_heater_mass,
recuperator_pche_mass, calculate_turbine_mass,
RecuperatorPCHEMassModel, calculate_water_outlet_temperature,
turbine_tac_mass, flow_velocity,
friction_factor,
gnielinski_f_star,
heat_transfer_coefficient,
nusselt_number,
overall_heat_transfer_coefficient,
pche_channel_flow_area,
pche_channel_wetted_perimeter,
pche_equivalent_diameter,
prandtl_number,
pressure_drop_pa,
reynolds_number,
validate_pche_state,
) )
from .optimization import ( from .optimization import (
optimize_rc_fixed_param, optimize_rc_fixed_param,
@@ -44,24 +56,36 @@ __all__ = [
"Concentrator", "Concentrator",
"Condenser", "Condenser",
"Heater", "Heater",
"PCHEGeometry",
"PCHEMaterial",
"PCHEDesignOptions",
"PCHE_STATE_UNIT_CONVENTION",
"Recuperator", "Recuperator",
"RecuperatorPCHEMassModel",
"Turbine", "Turbine",
"apply_component_mass", "calculate_condenser_mass",
"calculate_component_mass", "calculate_heater_mass",
"condenser_radiator_mass", "calculate_turbine_mass",
"calculate_water_outlet_temperature",
"flow_velocity",
"friction_factor",
"gnielinski_f_star",
"heat_transfer_coefficient",
"evaluate_rc_efficiency", "evaluate_rc_efficiency",
"has_default_mass_model",
"heater_reactor_mass",
"initialize_recuperator_segments",
"local_rc_component_performance_sensitivity", "local_rc_component_performance_sensitivity",
"local_rc_design_sensitivity", "local_rc_design_sensitivity",
"nusselt_number",
"optimize_rc_fixed_param", "optimize_rc_fixed_param",
"optimize_rc_param", "optimize_rc_param",
"overall_heat_transfer_coefficient",
"pche_channel_flow_area",
"pche_channel_wetted_perimeter",
"pche_equivalent_diameter",
"plot_optimization_landscape", "plot_optimization_landscape",
"plot_sweep_optimization_results", "plot_sweep_optimization_results",
"recuperator_pche_mass", "prandtl_number",
"pressure_drop_pa",
"reynolds_number",
"scan_rc_efficiency", "scan_rc_efficiency",
"sweep_and_optimize_rc", "sweep_and_optimize_rc",
"turbine_tac_mass", "validate_pche_state",
] ]
+90 -18
View File
@@ -1,9 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Component models used by Brayton cycle simulations.""" """Component models used by Brayton cycle simulations."""
from .mass_models import (
calculate_condenser_mass,
calculate_heater_mass,
calculate_turbine_mass,
)
class ComponentMassMixin: class ComponentMassMixin:
"""Shared mass-result storage interface for cycle components.""" """Shared mass-calculation interface for cycle components."""
component_type = "component" component_type = "component"
@@ -11,16 +17,43 @@ class ComponentMassMixin:
self.mass = None self.mass = None
self.mass_variables = None self.mass_variables = None
def set_mass_result(self, result): def calculate_mass(self, mass_model=None, **kwargs):
"""Store a mass-model result on this component. """Calculate and store component mass.
Mass formulas live in ``brayton_cycle.mass_models``. Components only The future mass model can be provided as either a callable accepting
keep the result after their thermodynamic calculator has populated ``(component, **kwargs)`` or an object exposing ``calculate_mass`` or
``variables``. ``calculate_<component_type>_mass``.
""" """
if self.variables is None: if self.variables is None:
raise ValueError("Run component calculator before storing mass") raise ValueError("Run component calculator before mass calculation")
if mass_model is None:
result = self._calculate_mass(**kwargs)
else:
result = self._run_external_mass_model(mass_model, **kwargs)
return self._store_mass_result(result)
def mass_calculator(self, mass_model=None, **kwargs):
return self.calculate_mass(mass_model=mass_model, **kwargs)
def _calculate_mass(self, **kwargs):
raise NotImplementedError(
f"{self.__class__.__name__} mass model is not implemented yet. "
"Pass a mass_model or override _calculate_mass()."
)
def _run_external_mass_model(self, mass_model, **kwargs):
method_name = f"calculate_{self.component_type}_mass"
if hasattr(mass_model, method_name):
return getattr(mass_model, method_name)(self, **kwargs)
if hasattr(mass_model, "calculate_mass"):
return mass_model.calculate_mass(self, **kwargs)
if callable(mass_model):
return mass_model(self, **kwargs)
raise TypeError("mass_model must be callable or expose a supported method")
def _store_mass_result(self, result):
if isinstance(result, dict): if isinstance(result, dict):
if "mass" in result: if "mass" in result:
mass = result["mass"] mass = result["mass"]
@@ -39,17 +72,8 @@ class ComponentMassMixin:
self.variables["mass_variables"] = self.mass_variables self.variables["mass_variables"] = self.mass_variables
return self.mass return self.mass
def clear_mass_result(self): class Compressor():
self.mass = None
self.mass_variables = None
if self.variables is not None:
self.variables.pop("mass", None)
self.variables.pop("mass_variables", None)
class Compressor(ComponentMassMixin):
"""压缩机类""" """压缩机类"""
component_type = "compressor"
def __init__(self, name, eff): def __init__(self, name, eff):
""" """
初始化参数 初始化参数
@@ -65,7 +89,6 @@ class Compressor(ComponentMassMixin):
self.name = name self.name = name
self.eff = eff self.eff = eff
self.variables = None self.variables = None
self._init_mass_interface()
def calculator(self, p_in, T_in, p_out, property_calculator): def calculator(self, p_in, T_in, p_out, property_calculator):
# 先计算熵值 # 先计算熵值
@@ -144,6 +167,9 @@ class Turbine(ComponentMassMixin):
'pi': p_in / p_out 'pi': p_in / p_out
} }
def _calculate_mass(self, Pe=None, A=0.5, **kwargs):
return calculate_turbine_mass(self, Pe=Pe, A=A, **kwargs)
class Recuperator(ComponentMassMixin): class Recuperator(ComponentMassMixin):
"""换热器类""" """换热器类"""
component_type = "recuperator" component_type = "recuperator"
@@ -334,6 +360,23 @@ class Heater(ComponentMassMixin):
'Q_in': Q_input, 'Q_in': Q_input,
} }
def _calculate_mass(
self,
P_heat_mwt=None,
mass_flow_rate=None,
shielding_mass_ton=2.8,
include_shielding=True,
**kwargs,
):
return calculate_heater_mass(
self,
P_heat_mwt=P_heat_mwt,
mass_flow_rate=mass_flow_rate,
shielding_mass_ton=shielding_mass_ton,
include_shielding=include_shielding,
**kwargs,
)
class Condenser(ComponentMassMixin): class Condenser(ComponentMassMixin):
"""冷凝器类""" """冷凝器类"""
component_type = "condenser" component_type = "condenser"
@@ -362,6 +405,35 @@ class Condenser(ComponentMassMixin):
'Q_out': Q_output, 'Q_out': Q_output,
} }
def _calculate_mass(
self,
Qc_kw=None,
co2_mass_flow_rate=None,
coolant_outlet_T=None,
water_inlet_T=None,
water_mass_flow_rate=None,
water_pressure_kpa=101.325,
refprop_path="C:/Program Files (x86)/REFPROP 10.0+/REFPROP",
emissivity=0.92,
surface_temperature=210.15,
area_density=6.75,
**kwargs,
):
return calculate_condenser_mass(
self,
Qc_kw=Qc_kw,
co2_mass_flow_rate=co2_mass_flow_rate,
coolant_outlet_T=coolant_outlet_T,
water_inlet_T=water_inlet_T,
water_mass_flow_rate=water_mass_flow_rate,
water_pressure_kpa=water_pressure_kpa,
refprop_path=refprop_path,
emissivity=emissivity,
surface_temperature=surface_temperature,
area_density=area_density,
**kwargs,
)
class Concentrator(ComponentMassMixin): class Concentrator(ComponentMassMixin):
"""汇流组件""" """汇流组件"""
component_type = "concentrator" component_type = "concentrator"
+6 -30
View File
@@ -9,7 +9,6 @@ from .components import (
Recuperator, Recuperator,
Turbine, Turbine,
) )
from .mass_models import apply_component_mass, has_default_mass_model
from .properties import CO2PropertyCalculator from .properties import CO2PropertyCalculator
@@ -58,31 +57,13 @@ class BraytonCycle:
else: else:
yield value yield value
def calculate_component_masses(self, mass_model=None, strict=False, **kwargs): def calculate_component_masses(self, mass_model=None, **kwargs):
"""Calculate mass for each initialized component.""" """Calculate mass for each initialized component."""
component_masses = {} component_masses = {}
for component in self.iter_components(): for component in self.iter_components():
if not hasattr(component, "set_mass_result"): if not hasattr(component, "calculate_mass"):
continue continue
if mass_model is None and not has_default_mass_model(component): mass = component.calculate_mass(mass_model=mass_model, **kwargs)
if strict:
raise NotImplementedError(
f"No default mass model for component type "
f"{component.component_type!r}."
)
continue
try:
mass = apply_component_mass(
component,
mass_model=mass_model,
**kwargs,
)
except NotImplementedError:
if strict:
raise
continue
key = component.variables.get("name", component.name) key = component.variables.get("name", component.name)
component_masses[key] = { component_masses[key] = {
"component_type": component.component_type, "component_type": component.component_type,
@@ -93,23 +74,18 @@ class BraytonCycle:
self.component_masses = component_masses self.component_masses = component_masses
return self.component_masses return self.component_masses
def cycle_mass_calculator(self, mass_model=None, strict=False, **kwargs): def cycle_mass_calculator(self, mass_model=None, **kwargs):
"""Calculate and return total cycle component mass.""" """Calculate and return total cycle component mass."""
component_masses = self.calculate_component_masses( component_masses = self.calculate_component_masses(
mass_model=mass_model, mass_model=mass_model,
strict=strict,
**kwargs, **kwargs,
) )
self.total_mass = sum(item["mass"] for item in component_masses.values()) self.total_mass = sum(item["mass"] for item in component_masses.values())
return self.total_mass return self.total_mass
def calculate_total_mass(self, mass_model=None, strict=False, **kwargs): def calculate_total_mass(self, mass_model=None, **kwargs):
"""Compatibility alias for cycle_mass_calculator.""" """Compatibility alias for cycle_mass_calculator."""
return self.cycle_mass_calculator( return self.cycle_mass_calculator(mass_model=mass_model, **kwargs)
mass_model=mass_model,
strict=strict,
**kwargs,
)
def SC(self, T_low, T_high, p_low, p_high, param=None): def SC(self, T_low, T_high, p_low, p_high, param=None):
"""Simple Brayton cycle.""" """Simple Brayton cycle."""
File diff suppressed because it is too large. Load diff
+28 -11
View File
@@ -15,7 +15,7 @@ class CO2PropertyCalculator():
self.rp.SETUPdll(2, 'SI', 'SI', 'DEF') self.rp.SETUPdll(2, 'SI', 'SI', 'DEF')
self.z = [1.0] self.z = [1.0]
self.mw = self.rp.WMOLdll(self.z) self.mw = self.rp.WMOLdll(self.z)
def calculate_properties(self, T=None, P=None, h=None, s=None): def calculate_properties(self, T=None, P=None, h=None, s=None, include_transport=False):
"""计算二氧化碳物性""" """计算二氧化碳物性"""
if T is not None and P is not None: if T is not None and P is not None:
# 已知Tp # 已知Tp
@@ -60,15 +60,32 @@ class CO2PropertyCalculator():
# 补充提取的物性,这里由于后续还要使用,不进行参数变换 # 补充提取的物性,这里由于后续还要使用,不进行参数变换
properties['D'] = result.D properties['D'] = result.D
properties['density_kg_m3'] = result.D * self.mw
properties['cp'] = result.Cp properties['cp'] = result.Cp
properties['cp_j_kg_k'] = result.Cp / self.mw * 1000 properties['cv'] = result.Cv,
properties['cv'] = result.Cv properties['rho'] = result.D * self.mw
properties['cv_j_kg_k'] = result.Cv / self.mw * 1000 properties['cp_mass'] = result.Cp / self.mw * 1000.0
properties['cv_mass'] = result.Cv / self.mw * 1000.0
transport = self.rp.TRNPRPdll(result.T, result.D, self.z) if include_transport:
if transport.ierr > 0: self._add_transport_properties(properties, result)
raise ValueError(f"REFPROP transport calculation error:{transport.ierr}")
properties['viscosity_pa_s'] = transport.eta * 1e-6
properties['thermal_conductivity_w_m_k'] = transport.tcx
return properties return properties
def _add_transport_properties(self, properties, result):
"""Add CO2 transport properties needed by PCHE correlations."""
transport = self.rp.TRNPRPdll(properties['T'], result.D, self.z)
if getattr(transport, 'ierr', 0) > 0:
raise ValueError(f"REFPROP transport calculation error:{transport.ierr}")
viscosity_micro_pa_s = getattr(transport, 'eta', None)
thermal_conductivity = getattr(transport, 'tcx', None)
if viscosity_micro_pa_s is None:
viscosity_micro_pa_s = getattr(transport, 'visc', None)
if thermal_conductivity is None:
thermal_conductivity = getattr(transport, 'tcond', None)
if viscosity_micro_pa_s is None or thermal_conductivity is None:
raise ValueError("REFPROP transport result missing viscosity or conductivity")
viscosity_pa_s = viscosity_micro_pa_s * 1.0e-6
properties['mu'] = viscosity_pa_s
properties['viscosity'] = viscosity_pa_s
properties['lambda'] = thermal_conductivity
properties['thermal_conductivity'] = thermal_conductivity