Managing Entity References Within an Entity for ECS Fluid Simulation

Hi everyone,

I’m relatively new to ECS and I’m in the process of rewriting my object-oriented code using DOTS (Data-Oriented Technology Stack). I’m currently working on modeling fluid exchange within a valve system. I have entities representing areas and valves. Each area entity contains components like “AreaGas”," AreaLiquid", and “AreaConfiguration”, where I store parameters such as pressure, temperature, and density. On the other hand, valve entities contain a single component:

    public struct FluidRestriction : IComponentData
    {
        public Entity Input;
        public Entity Output;
        public float Kv;
        public float Flow;
    }

I want to associate area entities with the “Input” and “Output” fields of the “FluidRestriction” component. Before starting the simulation, I establish these connections between input and output areas for each valve entity.

During simulation, I need to calculate the flow rate based on the pressure difference between the input and output areas. However, accessing the parameters inside the area components (“AreaGas”, “AreaLiquid”, etc.) using “GetComponentData” within a job that requires Burst compilation and parallel scheduling poses a challenge.

Here’s a snippet of how I’m currently accessing the area components inside a system:

            Entities
              .WithAll<FluidRestriction>()
              .ForEach((Entity entity, ref FluidRestriction restriction) =>
              {
                  var inputGas = EntityManager.GetComponentData<AreaGas>(restriction.Input);
                  var outputGas = EntityManager.GetComponentData<AreaGas>(restriction.Output);
              })
             .WithBurst()
             .ScheduleParallel();

Unfortunately, using “GetComponentData” with Burst compilation and parallel scheduling is not supported.

Does anyone have suggestions on how to efficiently access the area components (“AreaGas”, “AreaLiquid”, etc.) within this context to calculate fluid flow rates? Any advice or alternative approaches would be greatly appreciated. Thank you!

Switch to IJobEntity and use a [ReadOnly] ComponentLookup which you can fetch from the main thread using SystemAPI.

1 Like

Hi DreamingImLatios,

Thank you for your response! :wink:

I’ve tried your suggestions and reviewed the documentation on “ComponentLookup.” Unfortunately, the Valve script writing the new pressure calculations directly to the areas is causing issues. :frowning:

As you mentioned, ComponentLookup should ideally be used in a read-only manner. It seems like I may need to reconsider the logic of my scripts to ensure proper data handling within the ECS framework.

You can write data directly into a ComponentLookup but it’s not safe as multithreaded if you can’t guarantee that you’re only writing to one unique entry for each entity that you’re iterating. This is still faster than non DOTS code if Burst compiled.

1 Like

Hi Davenirline,

Thank you so much for your helpful response!

I understand what you’re talking about. I tried using ComponentLookup, but I’m unsure how to set up my job variables outside the job to access the area components through my valve entity.

To clarify, I’ve shared my system code:

    [UpdateAfter(typeof(AreaInitSystem))]
    public partial class ValveSystem : SystemBase
    {
        protected override void OnCreate()
        {

        }

        protected override void OnStartRunning()
        {
        }

        protected override void OnUpdate()
        {
            var deltaTime = SystemAPI.Time.DeltaTime;
            var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
            var ecb = ecbSingleton.CreateCommandBuffer(World.Unmanaged).AsParallelWriter();

            new ValveJob
            {
                ECB = ecb,
                DeltaTime = deltaTime,
                //How to set inputGas and outputGas here thought valve entity????
                //inputGas = SystemAPI.GetComponent(Valve.Input),???
                //OutputGas = SystemAPI.GetComponent(Valve.Output),???
            }.ScheduleParallel();

        }
    }

And here is my job code:

[BurstCompile]
public partial struct ValveJob : IJobEntity
{
    public EntityCommandBuffer.ParallelWriter ECB;
    public float DeltaTime;

    [ReadOnly] public ComponentLookup<AreaGas> inputGas;
    [ReadOnly] public ComponentLookup<AreaGas> outputGas;

    [BurstCompile]
    private void Execute(ValveAspect valveAspect, [EntityIndexInQuery] int sortKey)
    {
// Need to access inputGas and outputGas here
    }
}

I’m struggling with how to properly use ComponentLookup. Could you please provide guidance on how to set inputGas and outputGas variables?

Thank you again for your patience and assistance! I’m eager to resolve this issue. :sweat_smile:

How do you determine if an AreaGas is input or output? Is it a boolean flag? Or another component?

1 Like

My Valve entity has both an input and an output, both of which are entities containing components like AreaGas, AreaLiquid, and AreaConfiguration :

Here is my Authoring and Baker for Valve :

    public class FluidsRestrictionAuthoring : MonoBehaviour
    {
        public GameObject Input;
        public GameObject Output;
        public float Kv;
        [ReadOnly] public float Flow;

        class Baker : Baker<FluidsRestrictionAuthoring>
        {
            public override void Bake(FluidsRestrictionAuthoring authoring)
            {
                var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);

                AddComponent(entity, new FluidRestriction
                {
                    Input = GetEntity(authoring.Input, TransformUsageFlags.None),
                    Output = GetEntity(authoring.Output, TransformUsageFlags.None),
                    Kv = authoring.Kv,
                    Flow = authoring.Flow,
                });
            }
        }
    }

Let me know that clarify things for you now :wink:
Thank again for your help !

I see. Then you use FluidRestriction.Input and FluidRestriction.Output to access the AreaGas component:

public ComponentLookup<AreaGas> allAreaGas;

public void Execute(FluidRestriction restriction) {
    AreaGas input = allAreaGas[restriction.Input];
    AreaGas output = allAreaGas[restriction.Output];

    ...
}
1 Like

Ok, thank you so much for this explanation. I will try to manage my simulation using all this information!