Voxel/Cube based realistic water/fluid physics in Unity

Hello

i am creating a 3D Voxel based game(voxels are arranged in 3D array), where the game area is divide in chunks. I am trying to implement somewhat realistic water physics based on voxels of type water that can hold water within.
Water should be able to:

  1. spread across horizontally, example, if I place 1 full water voxel in a box 2x2 then after physics calculations should be done there should be ~ 0.25 water in each.
  2. flow vertically down like waterfalls
  3. (optional) ideally I would want in some interaction to make the fluid have density and pressure, and momentum

i have tried for a few days myself in multiple iterations with something like this

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

[BurstCompile]
public struct WaterFlowJob : IJob {
    public NativeArray<Voxel> voxels;
    public int chunkSize;
    public float deltaTime;

    private const int IterationsPerExecute = 3; 
    private const float MaxFlowRate = 0.1f; 
    private const float MinWaterThreshold = 0.01f; 

    public void Execute() {

        NativeArray<bool> isInitialWaterBlock = new NativeArray<bool>(voxels.Length, Allocator.Temp);
        NativeArray<bool> updatedVoxels = new NativeArray<bool>(voxels.Length, Allocator.Temp); 

        
        for (int i = 0; i < voxels.Length; i++) {
            if (voxels[i].type == VoxelType.Water && voxels[i].waterLevel >= MinWaterThreshold) {
                isInitialWaterBlock[i] = true;
            } else {
                isInitialWaterBlock[i] = false;
            }
        }

        for (int iteration = 0; iteration < IterationsPerExecute; iteration++) {
            
            for (int y = 0; y < chunkSize; y++) {
                for (int x = 0; x < chunkSize; x++) {
                    for (int z = 0; z < chunkSize; z++) {
                        int index = GetIndex(x, y, z, chunkSize);
                        if (!isInitialWaterBlock[index]) {
                            continue; 
                        }
                        

                        Voxel currentVoxel = voxels[index];

                        if (voxels[index].type == VoxelType.Water && currentVoxel.waterLevel < MinWaterThreshold) {
                            currentVoxel.isActive = false;
                            currentVoxel.type = VoxelType.Air;
                            voxels[index] = currentVoxel;
                        }

                        Vector3Int[] directions = { Vector3Int.left, Vector3Int.right, Vector3Int.forward, Vector3Int.back };

                        foreach (var dir in directions) {
                            int neighborX = x + dir.x;
                            int neighborZ = z + dir.z;

                            if (IsInBounds(neighborX, y, neighborZ, chunkSize)) {
                                int neighborIndex = GetIndex(neighborX, y, neighborZ, chunkSize);
                                Voxel neighborVoxel = voxels[neighborIndex];

                                if (neighborVoxel.type == VoxelType.Water &&
                                        neighborVoxel.waterLevel < currentVoxel.waterLevel &&
                                        (currentVoxel.waterLevel - neighborVoxel.waterLevel) > MinWaterThreshold) {

                                    
                                    float flowAmount = Mathf.Max((currentVoxel.waterLevel - neighborVoxel.waterLevel) * MaxFlowRate, MinWaterThreshold);
                                    if (flowAmount > 0) {
                                        Voxel tempNeighborVoxel = voxels[neighborIndex];
                                        tempNeighborVoxel.waterLevel += flowAmount;
                                        currentVoxel.waterLevel -= flowAmount;

                                        voxels[neighborIndex] = tempNeighborVoxel;
                                        voxels[index] = currentVoxel;
                                    }
                                }

                                if (!updatedVoxels[neighborIndex]) {
                                    

                                    if (neighborVoxel.type == VoxelType.Air) {
                                        float flowAmount = Mathf.Max(currentVoxel.waterLevel * MaxFlowRate, MinWaterThreshold);
                                        if (flowAmount > 0) {
                                            Voxel tempNeighborVoxel = voxels[neighborIndex];
                                            tempNeighborVoxel.isActive = true;
                                            tempNeighborVoxel.type = VoxelType.Water;
                                            tempNeighborVoxel.waterLevel += flowAmount;
                                            currentVoxel.waterLevel -= flowAmount;

                                            voxels[neighborIndex] = tempNeighborVoxel;
                                            voxels[index] = currentVoxel;

                                            updatedVoxels[neighborIndex] = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        isInitialWaterBlock.Dispose();
        updatedVoxels.Dispose(); 
        
    }

    private int GetIndex(int x, int y, int z, int size) {
        return x * size * size + y * size + z;
    }

    private bool IsInBounds(int x, int y, int z, int size) {
        return x >= 0 && x < size && y >= 0 && y < size && z >= 0 && z < size;
    }
}

But i am not sure if I am going in the right direction. I us IJob so that water calculations are done in the background. The world would not be infinite but I want to create it so that it can also run on mobile devices.

Any suggestions on how to better do this. I am new to developing voxel based games, and to game development in general but have been a programmer a while. Maybe there are approaches i cant think about.