Distributed Bi Directional Flow Fluid Interface - nasa/gunns GitHub Wiki

Table of Contents

Introduction

This page describes the generic bi-directional flow fluid interface provided with the GUNNS repository. This is a generic C++ model to integrate two separate fluid models at an interfacing volume. This code has no dependencies on GUNNS, Trick, or any other libraries besides ANSI-standard C++. It is intended for use in both GUNNS/Trick and non-GUNNS/Trick simulations and fluid models.

The code for this interface is in gunns/core/GunnsFluidDistributed2WayBus.hh, .cpp, and its base class is in gunns/core/GunnsDistributed2WayBusBase.hh, .cpp. For use in GUNNS models, this interface is wrapped by the GUNNS link GunnsFluidDistributedIf, which takes care of most of the additional considerations, such as capacitance and applying the boundary conditions, automatically. Refer to its link help page: GunnsFluidDistributedIf for use in GUNNS models. But this Wiki article is mainly intended for the non-GUNNS users. Non-GUNNS models should wrap the GunnsFluidDistributed2WayBus code in a custom code layer that integrates it into their model and simulation environment. Then there are additional considerations for operating it, detailed in this article.

For each fluid connection between distributed models, a GunnsFluidDistributed2WayBus is instantiated in each model, on its side of the interface. The GunnsFluidDistributed2WayBus is designed to integrate with another instance of itself, its counterpart in the other model. They communicate with data objects that are transported between them by the distributed simulation data network such as High Level Architecture or Functional Mock-up Interface. The paired GunnsFluidDistributed2WayBus negotiate and handshake which side owns the state of the interface volume (the Supply role), while the other side (the Demand role) computes flow between its model and the interface volume. This allows fluid to flow between the models through the interface in either direction, from the high-pressure side to the low-pressure side. More on the Supply & Demand roles below.

Distributed Models Example

Below we illustrate an example fluid model for the combined 'ISS' and 'VV' vehicles. The two vehicles are docked, creating 2 interface volumes between them - the main 'vestibule', between their hatches, through which the astronauts can pass, and the 'IMV duct', a ventilation duct to help air circulation between them. The IMV duct has an inline fan on the ISS side, and the ISS side also has an inline Atmosphere Revitalization System (ARS) that purifies the air. The fan pushes air towards the VV side, creating an air circulation loop through the IMV ducts, open valves and hatches in the clockwise direction, as shown:

Distributed_Fluid_Combined_Model

Now we break these up into separate simulations and models for the respective vehicles, with our GunnsFluidDistributed2WayBus to interface them, shown as the blue <-> symbols. The red dashed lines represent the HLA data messages transferred between the GunnsFluidDistributed2WayBus pairs. Each interfacing volume (vestibule and IMV duct) is represented in both simulations, and how they are represented in each simulation is controlled by the GunnsFluidDistributed2WayBus attached to them.

Distributed_Fluid_Separate_Models

Quick Summary of Use

This illustrates the basic flow of data and operations for each step of the models. Here we are just showing the vestibule interface, and omitting the IMV duct interface for clarity, but the concept is the same for each interfacing pair of GunnsFluidDistributed2Waybus:

Distributed_Fluid_Data_Loop

Supply and Demand Roles

Each side of the GunnsFluidDistributed2Waybus pair takes one of two roles: Supply or Demand:

  • The roles can swap during runtime, to keep the Supply role on the model side that has the larger fluid capacitance. This helps ensure stability between the models in the presence of transport lag around the data exchange network.
  • The pair of GunnsFluidDistributed2Waybus negotiate these role swaps between themselves, requiring no action from the user other than supplying the local model's capacitance to its GunnsFluidDistributed2Waybus.
    • The roles swap when the Supply side finds itself to have less than 80% of the Demand side's capacitance. This 80% threshold provides a buffer to avoid excess role swapping when the two sides have similar capacitance and the capacitances are noisy, while still providing enough stability. The worst-case stability scenario is when the Supply side has a steady capacitance right above the 80% threshold, with large data transport lag around the data exchange network. In such a case, the Demand side's flow rate limit (described below) is at its smallest value, producing the highest restriction on the flow rate, which ensures stability.
  • The Supply and Demand roles do not imply flow direction. Fluid always flows across the interface from the higher pressure to the lower pressure side, regardless of these roles.
  • The fluid flow rate value that is sent from the Demand role to the Supply role has a sign convention that indicates the flow direction: positive values are flow from the Supply side to the Demand side. In the example picture above, where the air is flowing through the vestibule volume interface from the Demand to the Supply side, this value would be negative.
  • The Supply role owns the state of the fluid in the interface volume, while the Demand role owns the fluid flow rate across the interface.
  • The Supply role sends the state of the fluid in the interface volume to the Demand role, which applies it as a boundary condition to its local model.
  • The Demand role sends the rate of fluid flow across the interface to the Supply role, which applies it as a boundary condition to its local model.
  • The Demand role calculates a limit on the maximum absolute value of the interface flow rate to ensure stability between the models.
    • This limit is a function of the relative capacitances of both sides, and the measured transport lag around the data exchange network.
    • The limit reduces as the Supply-side capacitance shrinks relative to the Demand-side capacitance, and it reduces as the transport lag increases.

Interfaces

Local Code Interfaces

This summarizes the operations in the interface between the local model and the GunnsFluidDistributed2Waybus. Taken from the GunnsFluidDistributed2Waybus.hh comments: The order of operations between the local model and this object during each model step should be:

  1. this->mInData updated with incoming data from the interface data network.
  2. Model calls this->processInputs()
  3. Model calls this->isInDemandRole(), responds to role swap as needed.
  4. Model calls this->getFluidState() or getFlowState() based on role.
  5. Model applies the returned fluid or flow state boundary conditions to its interface volume.
  6. If in Demand role, model calls this->computeDemandLimit()
  7. Model does its main update. If in Demand role, applies the demand flow rate limit.
  8. Model calculates its local capacitance at the interface.
  9. Model calls this->setFluidState() or setFlowState() based on role.
  10. Model calls this->processOutputs()
  11. Model calls this->isInDemandRole(), responds to role swap as needed.
  12. Model calls this->popNotification() recursively until no notifications left, transfers notification to the sim's messaging system as desired.
  13. this->mOutData transmitted across the interface data network to the other side.

This list details the public attributes and methods in GunnsFluidDistributed2Waybus and its base class GunnsFluidDistributed2WayBusBase that the local model should access:

  • void initialize(const bool isPairMaster, const unsigned int nIfBulk, const unsigned int nIfTc): This initializes the interface and should be called exactly once during sim startup. The isPairMaster tells this side of the interface whether it is the 'master' side of the pair - both sides should negotiate in advance who is the master and who is not. This is only used for a tiebreaker in negotiations of the initial Supply/Demand roles between the sides. The nIfBulk and nIfTc arguments specify how big the bulk fluid and trace compound mixture arrays are in the data exchange network (HLA FOM, etc.)
  • void processInputs(): This should be called once each local model step, to process the input data received in mInData from the data exchange network. This updates the frame counters in mOutData, computes the measured data transport lag, and does some of the role swap logic. This is where we'll switch from Demand to Supply if the other side has initiated its swap from Supply to Demand. The result of this call is that the interface can be in a different role, so this call should be followed by calling isInDemandRole() to confirm which role we're now in.
  • bool isInDemandRole(): Returns true if this interface is currently in the Demand role, and false if it is in the Supply role.
  • bool getFluidState(GunnsFluidDistributed2WayBusFluidState& fluid): Sets the supplied FluidState object reference equal to the received Supply state from the other side, when we are in the Demand role. This returns true if the supplied FluidState object was updated, otherwise returns false. If you call this function when in the Supply role, this will not update the supplied FluidState, and will return false. Other reasons why this would return false is if the incoming data is not valid, or does not represent Supply mode data from the other side.
    • More on the boundary condition: if this function returns true, the local model should apply the resulting FluidState object's values to the pressure/energy/mixture boundary condition on the interface volume, in the Demand mode. This means setting the pressure, energy (temperature or specific enthalpy, explained below) and mixture in the interface volume to these values. This overrides whatever values the Demand model previously had in the interface volume. The pressure should be treated as an ideal pressure source, and does not change in response to flows into or out of the interface volume from the Demand-side model. Because the pressure in the Demand-side is constrained by the Supply-side value, then the interface volume on the Demand side effectively has infinite volume. The only limit on how much flow can leave or enter it from the Demand-side model is the limit provided by the computeDemandLimit function, detailed below.
  • bool getFlowState(GunnsFluidDistributed2WayBusFlowState& flow): Sets the supplied FlowState object reference equal to the received Demand state from the other side, when we are in the Supply role. This returns true if the supplied FlowState object was updated, otherwise returns false. If you call this function when in the Demand role, this will not update the supplied FlowState, and will return false. Other reasons why this would return false is if the incoming data is not valid, or does not represent Demand mode data from the other side.
    • More on the boundary condition: if this return true, the local model should apply the resulting FlowState object's values to the flow boundary condition on the interface volume, in the Supply role. This means integrating the flow rate into an amount of moles to subtract from the interface volume. The flow rate sign is positive for moles leaving the Supply-side. For flow added to the Supply side (negative values of flow rate), the moles should be added with the FlowState object's energy and mixture values. For flow removed from the Supply side, the FlowState object's energy and mixture values are ignored.
  • double computeDemandLimit(const double timestep, const double demandSidePressure): This should be called by the model when in the Demand role, to return the limit that the model should apply to its calculation of flow rate between the local model and the interface location. The timestep argument is the timestep of the local model udpate. The demandSidePressure argument is the pressure on the Demand side of the interface location. In the picture above, this would be the 'VV' volume across the hatch from the VV vestibule volume.
  • void setFluidState(const GunnsFluidDistributed2WayBusFluidState& fluid): Copies the given FluidState of the Supply side volume to the mOutData object for output to the data exchange network. This should only be called in the Supply role. If called when in the Demand role, this interface will add a warning message to its notification queue, and will not update its mOutData.
  • void setFlowState(const GunnsFluidDistributed2WayBusFlowState& flow): Copies the given FlowState between the Demand side model and the interface volume to the mOutData object for output to the data exchange network. This should only be called in the Demand role. If called when in the Supply role, this interface will add a warning message to its notification queue, and will not update its mOutData.
  • void processOutputs(const double capacitance): Loads the given capacitance value into mOutData, and swap roles from Supply to Demand role if the new capacitance value is <80% of the other side's capacitance. Because this call can result in the role changing, this should be followed by another call to isInDemandRole() to confirm the new role and adjust the local model if needed.
  • unsigned int popNotification(GunnsDistributed2WayBusNotification& notification): This sets the given Notification object reference equal to the notification data at the top of the notifications queue, and deletes the top notification from the queue. Finally, it returns the number of notifications left in the queue. Each pass of your local model, you can call this function recursively until it returns a queue size of zero, forwarding each notification to your simulation message system as desired.

Distributed Data Interfaces

Each GunnsFluidDistributed2Waybus has 2 instances of the GunnsFluidDistributed2WayBusInterfaceData class for communicating with its paired counterpart in the other simulation. These instances are called mOutData, for sending data out to the other side, and mInData, for receiving data from the other side. Each instance's mOutData should be written to the other side's mInData via the data exchange interface (HLA, etc.) When using HLA, these terms map directly to the HLA Federate Object Model (FOM). The data contents are driven by the GunnsFluidDistributed2Waybus, so the user doesn't need to interact with these. We detail the data attributes here for reference:

  • mDemandMode (bool) This side indicates whether the sender is in the Demand role (true) or Supply (false).
  • mFrameCount (uint32) The sender increments this integer by one each time it is updated. This is used in the data transport lag measurement.
  • mFrameLoopback (uint32) This is a reflection of the received mFrameCount back to the other side. This is used in the data transport lag measurement.
  • mCapacitance (double) This is the fluid capacitance (mol/Pa) of the local side model to flow. This is how many moles of fluid would be added to raise the local model pressure by one Pascal. See below for the link to a further description of this capacitance.
  • mSource (double) This represents the Supply pressure (Pa) or Demand flow rate (mol/s), depending on the Supply/Demand role of the sender.
  • mEnergy (double) This represents either fluid temperature (K) or specific enthalpy (J/kg) -- both sides should agree on which parameter to use, and use the same parameter on both sides.
  • mMoleFractions (double*) Array of mole fractions of the bulk constituents in the fluid mixture. Bulk constituents are those that contribute significantly to the mixed fluid properties, such as specific heat, specific enthalpy, etc. Both sides must agree on how many bulk compounds there are and in which order they are represented in this array. The sum of all mMoleFractions and mTcMoleFractions must always be exactly 1. All individual components in this array must always be >= 0.
  • mTcMoleFractions (double*) Array of mole fractions of the trace compounds in the fluid mixture. Trace compounds are those that are in low enough concentrations that their contribution to the mixed fluid properties can be ignored. Both sides must agree on how many trace compounds there are and in which order they are represented in this array. We usually consider any compound that will never have a concentration of more than 1 part per thousand to be a trace compound. So in a normal 'air' mixture, CO2 and H2O are bulk compounds, but NH3 and CHN are usually trace compounds. The sum of all mMoleFractions and mTcMoleFractions must always be exactly 1. All individual components in this array must always be >= 0.

See Also

Assumptions & Limitations

  • The distributed models do not have to update at the same rate. However, conservation of mass & energy is modeled more accurately if both models run at the same rate and use the same numerical integration of fluid flow. As an example, GUNNS fluid models traditionally run at 10 Hz (although this varies by project) and always use backwards-Euler integration.
  • Energy is not conserved if the Energy data exchange parameter represents temperature, instead of enthalpy, and the fluid properties models on both sides of the interface do not have the same temperature-enthalpy relationship. Therefore, to better conserve energy, it's recommended to transfer the Energy term as specific enthalpy. However, temperature can be directly sensed by real-world sensors, whereas enthalpy cannot. If you transfer enthalpy instead of temperature, and the fluid properties models are not the same, then a temperature sent by one side does not show up with the same temperature at the other side, and this can appear to your users as a standing temperature difference across the interface. So you must choose to either have a discrepancy in sensed temperatures or conservation of energy, unless both models have identical fluid properties. This is really a limitation of the fluid properties models and not this interface.
  • Mass and energy are briefly not conserved when the Supply & Demand roles swap while there is a flow rate. This is because the side initiating the role swap from Supply to Demand drops the subsequent demanded flow rates received from the other side, until the other side gets around to switching from Demand to Supply. The amount of mass & energy loss/gain is proportional to the flow rate and the transport lag around the data exchange loop. However, this is not a serious problem, because the role swaps should only occur rarely, when the models are undergoing large configuration changes.
  • Mass and energy are not conserved when the data exchange network (HLA, etc.) drops or doubles data frames.
  • In bad stability configurations, such as when Cs / Cd = 0.8 and large data transport lag, the demand flow rate limit applied by the Demand side can damp the flow to unrealistically low values.
  • This interface only supports bulk fluid flow across the interface, and doesn't support free molecular diffusion where different species diffuse in opposite directions based on their partial pressures.
⚠️ **GitHub.com Fallback** ⚠️