ThermofluidStream icon indicating copy to clipboard operation
ThermofluidStream copied to clipboard

Handling nonlinear flow-pressure relationships in gas system models (compressors and valves)

Open FrancNep opened this issue 1 month ago • 4 comments

Hello,

I’m currently working on the modeling of complex renewable gas production systems — for example, hydrogen production by high-temperature electrolysis. I have solid experience using the TFS in complex energy systems with incompressible fluids (e.g. district heating networks). However, I’m encountering several difficulties when working with complex gas systems. Here are two examples: • In our systems, we use positive displacement compressors whose fluid models and manufacturer data tables are laws of the form: V_flow = f(Speed, pout/pin) • Compressible valve models (e.g. the MSL one) use laws like: V_flow = Cv * Y * dp^2 with Y = f(dp/p)

For TFS, both models are problematic because expressing dp = f(V_flow) is complex and highly nonlinear. I have tested several approaches:

  1. Approach 1: Accept the nonlinearity and keep it confined to the component model.
  2. Approach 2: Remove the nonlinearity using first-order filters to smooth it out.
  3. Approach 3: Solve the nonlinearity locally with Modelica.Math.Nonlinear.solveOneNonlinearEquation.

All three approaches work fine for simple unidirectional hydraulic systems with one or two branches. For more complex systems, Approach 2 seems to perform best, but it is sensitive to solver parameterization and leads to long computation times due to the small time constants used in the filters.

I’d like to get your feedback and recommendations on which approach you consider the most efficient and robust to handle this kind of nonlinear behavior in gas systems.

Thanks in advance!

FrancNep avatar Nov 03 '25 13:11 FrancNep

When designing components for the TFS, one should ensure that the non-linearities are confined to the component and that the remaining non-linearity is well natured (can sometimes be difficult to judge...). This can be typically achieved by two means:

  • introduction of explicit formulas of previously implicitly formulated systems
  • the introduction of states

You have basically already done all of that. The first-order filter is introducing a state. Maybe it helps to reconsider the physics of the compressor and choose a physically meaningful state of the process that actually takes a bit of time to change also in reality. This can help to reduce stiffness.

Also having models that work on tables directly can be troublesome. Sometimes it is worth taking the effor of creating a nice analytical model and make it fit the data of the table.

dzimmer avatar Nov 06 '25 17:11 dzimmer

When I am using TFS to calculate blocking flow models, such as gas orifices and valves, and have encountered the same issue. The method I applied involves adding volume at the inlet and outlet positions. In the bidirectional flow model, the inlet and outlet pressures p_up_internal /p_down_internal are forced to be state variables, as shown in the following expressions. Although this resolves the nonlinearity issue, the pressure state variables in the volume oscillate during pressure step changes.

// Variables Modelica.Units.SI.Pressure r_rear_port "Inertial pressures at rear port"; Modelica.Units.SI.Pressure r_fore_port "Inertial pressures at fore port"; SI.Pressure r_up "Inertial pressure in the upstream volume"; SI.Pressure r_down "Inertial pressure in the downstream volume"; Modelica.Units.SI.MassFlowRate m_flow_rear "Mass flow rates at rear port"; Modelica.Units.SI.MassFlowRate m_flow_fore "Mass flow rates at fore port";

// Equations der(m_flow_rear) * L = r_rear_port - r_rear_intern - r_damping; der(m_flow_fore) * L = r_fore_port - r_fore_intern - r_damping; r_rear_intern = ThermofluidStream.Undirected.Internal.regStep(m_flow_rear, r_up, 0, m_flow_reg); r_fore_intern = ThermofluidStream.Undirected.Internal.regStep(m_flow_fore, r_down, 0, m_flow_reg); p_up_internal = state_in_rear.p + r_rear_port; p_down_internal = state_in_fore.p + r_fore_port; dM_up = m_flow_rear - m_flow; dM_up = Medium.density_derp_h(state_up_internal) * der(p_up_internal) * V; dM_down = m_flow + m_flow_fore; dM_down = Medium.density_derp_h(state_down_internal) * der(p_down_internal) * V;

Rongshangming avatar Nov 07 '25 01:11 Rongshangming

Thank you very much for your answers.

I will try adding states/volumes at the inlet and outlet of the valve model — this component is currently the one causing difficulties, as I have dozens of them to integrate in my system.

Another point that seems interesting to me is that it might help the solvers to define nominal values for the integration variables (mass flow rates). In a “gas” system, the flow rates are generally much lower — in my case on the order of 10⁻² to 10⁻³ kg/s — than the default value of 1 assumed by the solvers. It therefore seems logical that specifying more realistic nominal values could improve solver convergence.

It might be worth adding this parameter to the base SISO component — even though it can easily be defined when extending it.

Thanks again!

FrancNep avatar Nov 07 '25 08:11 FrancNep

Adding volumes is really the last resort. It should actually be avoided for those sort of components

Good states to select in order to break non-linearities are for instance variables as specific outlet enthalpy or the like.

If you really have to add volumes and want to fight the oscillations then keep in mind there is the option for virtual damping build in some volume models. This damping only affects the kinetic oscillation but causes no pressure drop for a steady stream.

dzimmer avatar Nov 07 '25 09:11 dzimmer