Issues with 3D Pipe Flow modeling Using Modulus

Hi everyone,

I am working on creating a forward solution model for a simple flow inside a pipe (3D channel) using Navier-Stokes library available in Modulus-Sym. The medium is Glycerin, and the details of the channel and fluid properties are as follows:

Channel and Fluid Details:

  • Channel length = 3 m
  • Channel radius = 0.1 m
  • Fluid density = 1260 kg/m3
  • Fluid kinematic viscosity = 0.00119 m2/s.
  • Inlet velocity = 0.06 m/s

Issues/Queries:

  1. Inlet Constraint Issue: When I checked the inlet constraint in the VTP file, the velocity at the inlet is not correctly applied. Instead of the expected 0.06 m/s, it shows a much lower value of 0.004 m/s.

     What could be causing this discrepancy?
    
  2. Unrealistic Flow and Pressure Distribution
    After inferencing, the results show unrealistic flow and pressure distributions, even though the training losses are low.
    Below is a figure comparing the results of Modulus and OpenFOAM


Flow velocity is mostly uniform.

Please help me understand why need to consider and missed in trained the model.
Code used to train model.

import os
import numpy as np
from sympy import Symbol, Eq
from stl import mesh  # Ensure numpy-stl is installed
import torch
import modulus.sym
from modulus.sym.hydra import to_absolute_path, instantiate_arch, ModulusConfig
from modulus.sym.solver import Solver
from modulus.sym.domain import Domain
from modulus.sym.geometry.primitives_3d import Box
from modulus.sym.geometry.primitives_2d import Rectangle
from modulus.sym.domain.constraint import PointwiseBoundaryConstraint, PointwiseInteriorConstraint
from modulus.sym.domain.inferencer import PointwiseInferencer
from modulus.sym.domain.monitor import PointwiseMonitor
from modulus.sym.key import Key
from modulus.sym.eq.pdes.navier_stokes import NavierStokes
from modulus.sym.utils.io import InferencerPlotter
from modulus.sym.geometry.primitives_3d import Cylinder
from modulus.sym.utils.io.vtk import var_to_polyvtk
from sympy import sqrt, Max, Abs


@modulus.sym.main(config_path="conf", config_name="config")
def run(cfg: ModulusConfig) -> None:
    # Fluid : Glycerin properties
    nu = 0.00119     # kinematic viscosity (m^2/s)
    rho = 1260       # density (kg/m^3)
    ns = NavierStokes(nu=nu, rho=rho, dim=3, time=False)
    
    # Define the geometry of the pipe
    length = 3       # Length of the pipe (m)
    radius = 0.1     # Radius of the pipe (m)
    inlet_vel = 0.06 # Maximum velocity at the center

    # Instantiate the neural network architecture
    flow_net = instantiate_arch(
        input_keys=[Key("x"), Key("y"), Key("z")],
        output_keys=[Key("u"), Key("v"), Key("w"), Key("p")],
        cfg=cfg.arch.fully_connected,
    )
    nodes = (
        ns.make_nodes()
        + [flow_net.make_node(name="flow_network")]
    )

    x, y, z = Symbol("x"), Symbol("y"), Symbol("z")
    
    pipe = Cylinder(
        center=(0, 0, 0),
        radius=radius,
        height=length,
    )

    # sample geometry for plotting in Paraview
    nr_points = 100000     # number of points to sample
    s = pipe.sample_boundary(nr_points=nr_points)
    var_to_polyvtk(s, "boundaryChecking")

    # Create simulation domain
    domain = Domain()


    # Inlet boundary condition
    inlet_bc = PointwiseBoundaryConstraint(
        nodes=nodes,
        geometry=pipe,
        outvar={"u": 0, "v": 0, "w":inlet_vel },  # Pass the velocity components
        batch_size=cfg.batch_size.Inlet,
        lambda_weighting={"u": 1.0 - 20 * Abs(x), "v": 1.0 - 20 * Abs(y), "w":1},  # weight edges to be zero
        criteria=Eq(Symbol("z"), -1.5),  # Apply at the inlet (z = -1.5)
    )
    domain.add_constraint(inlet_bc, "inlet")

    # Outlet boundary condition (zero pressure)
    outlet_bc = PointwiseBoundaryConstraint(
        nodes=nodes,
        geometry=pipe,
        outvar={"p": 0},
        batch_size=cfg.batch_size.Outlet,
        criteria=Eq(Symbol("z"), 1.5),  # Outlet at z = 1.5
        lambda_weighting={"p":1},  # weight edges to be zero
    )
    domain.add_constraint(outlet_bc, "outlet")

    # No-slip condition on the pipe walls
    no_slip_bc = PointwiseBoundaryConstraint(
        nodes=nodes,
        geometry=pipe,
        outvar={"u": 0, "v": 0, "w": 0},
        batch_size=cfg.batch_size.Wall,
        lambda_weighting={"u":1, "v":1, "w":1},  # weight edges to be zero
        # criteria=Abs(Symbol("x")**2 + Symbol("y")**2 - radius**2) < 1e-6,  # Updated criteria
        criteria=Eq((Symbol("x")**2 + Symbol("y")**2) - radius**2, 0),  # Updated criteria
    )
    domain.add_constraint(no_slip_bc, "no_slip")

    # Interior constraints for Navier-Stokes equations
    interior = PointwiseInteriorConstraint(
        nodes=nodes,
        geometry=pipe,
        outvar={"continuity": 0, "momentum_x": 0, "momentum_y": 0, "momentum_z": 0},
        batch_size=cfg.batch_size.Interior,
        lambda_weighting={
            "continuity": Symbol("sdf"),
            "momentum_x": Symbol("sdf"),
            "momentum_y": Symbol("sdf"),
            "momentum_z": Symbol("sdf"),
        },
    )
    domain.add_constraint(interior, "interior")
    # Create the solver
    slv = Solver(cfg, domain)

    # Solve the problem
    slv.solve()

if __name__ == "__main__":
    run()

Hi,
I would suggest trying a few techniques to guide Modulus predict the flow behavior effectively:

  1. Use parabolic velocity constraint
  2. Add an integral plane and impose a mass flow constraint (reference).
  3. Start with the default lambda_weighting settings.
  4. Give modulus enough time to train [~100k iterations] and monitor the pressure drop and losses

I’m not an expert—these are based on my trial-and-error experiences.

Please check aneurysm example and this discussion post by @npstrike for more insight.

This is a simplest problem but sometimes very complicated to solve using Modulus-sym.

I hope this helps!

1 Like

Hi,

Could you please check the constraint file (no_slip.vtp)? the criteria seems to be clipping the walls of the cylinder.

I recommend using a criteria in the z-direction to properly define the wall region:

criteria = And(Symbol("z") < 1.5, Symbol("z") > -1.5)
1 Like