Add grid points manually

I am trying to run a physics problem wherein only specific grid points are to be used for training the model with PDE based loss. In Modulus, the problem geometry needs to be defined, which is used to sample the points for training the model (correct me if I am wrong here). Is there a way to select the training points inside the geometry manually without being sampled automatically?

To be more precise, I want to construct a 1D domain with a circle centred at each x-location. I tried parameterizing the x-location of centre of circle with symbol ‘x’. But this approach resulted in an error. Is there a way to specify circle at each point on a 1D line which will be sampled by Modulus for training? Can this be achieved without manually constructing the grid? If not, then how can the construction of grid be achieved?

Hi @shubhamsp2195

For manually specifying input points I would suggest building a dictionary of numpy arrays or CSV file with the points you want to sample was well as the target values.

You can then use some of Modulus’ utilities to then construct a constraint:

We do this a lot for validation where we have data on a discrete set of points rather than a continuous domain (E.g. see the annular ring example).

Thank you @ngeneva for your your quick reply to my query. I apologize if my question was not clear in my previous post. I am trying to use loss based on PDEs only (no data-driven loss) which would mean that there are no target values to be specified in the dictionary of numpy arrays or CSV file(mentioned in your reply to first query). Is it possible to sample points for training in this unsupervised setting? This is how my domain looks like.

I want to construct a 1D domain with circles centered at each x-location of this domain. I tried parameterizing the x-location of center of circle with symbol ‘x’. The circles themselves must have a cloud of points which can be sampled randomly. But this approach resulted in an error. Is there a way to specify circle at each point on a 1D line which will be sampled by Modulus for training? Can this be achieved without manually constructing the grid? If not, then how can the construction of grid be achieved?

It would be better if I am more direct in this case. The problem that I am trying to tackle is the psedo-2 dimensional model of Li-ion battery (Newman, Doyle, 1995) wherein spherical active particles are present at each x-location of 1D domain. 5 PDEs are solved in the 1D domain and one PDE is solved in the spherical particle located at each point on 1D domain. These two systems of PDEs are coupled through boundary conditions. Following is the rough figure for reference.

image

Hi @shubhamsp2195

I am trying to use loss based on PDEs only (no data-driven loss) which would mean that there are no target values to be specified in the dictionary of numpy arrays or CSV file(mentioned in your reply to first query). Is it possible to sample points for training in this unsupervised setting?

Even for PDE loss training you have target values. These target values end up being the PDE residual being equal to zero. So using the the PointwiseConstraint.from_numpy() method, an example would be maybe I would have:

# Suppose solving using N-S
invar =   {'x': np.array(n,1), 'y': np.array(n,1)}
outvar = {'momentum_x': np.zeros(n, 1), 'momentum_y': np.zeros(n,1)}

this is still training with just PDE losses.

I want to construct a 1D domain with circles centered at each x-location of this domain. I tried parameterizing the x-location of center of circle with symbol ‘x’. The circles themselves must have a cloud of points which can be sampled randomly. But this approach resulted in an error. Is there a way to specify circle at each point on a 1D line which will be sampled by Modulus for training?

While I have not done this set up myself, I would image it is achievable using the geometry module. You could parameterize the center point of the circle. Then, in your constraint or geometry object, use the parameterization argument to define some sample range this center point could exist. If you can get this to work, the geometry module is well suited for these type of problems. There’s an example of this parameterization for a circle in the geometry section of our docs.

Thank you @ngeneva for sharing this information. I will try to implement my problem along similar lines.

Hi @ngeneva
This information helped me create a grid manually for my problem. I was trying to extend this to multidimensional arrays of the form

    # grid generation
    cs_ar = np.reshape(np.array([[[c_s_0 for i in range(Np_an)] for j in range(N_an)]for k in range(1)]), (1,N_an,Np_an,1))
    yp_ar = np.reshape(np.array([[[i*dy_a for i in range(Np_an)] for j in range(N_an)]for k in range(1)]), (1,N_an,Np_an,1))
    xp_ar = np.reshape(np.array([[[j*dx_a for i in range(Np_an)] for j in range(N_an)]for k in range(1)]), (1,N_an,Np_an,1))
    tp_ar0 = np.reshape(np.array([[[0.0 for i in range(Np_an)] for j in range(N_an)]for k in range(1)]), (1,N_an,Np_an,1))

    #Constraint
    IC_an = PointwiseConstraint.from_numpy(
        nodes=nodes,
        invar={"x":xp_ar, "y":yp_ar, "t":tp_ar0},
        outvar={"c_s":cs_ar},
        batch_size=4,
    )
    domain.add_constraint(IC_an, "IC_an")

But the execution is stalling before the training starts

[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - Arch Node: battery_network has been converted to a FuncArch node.
[05:49:23] - attempting to restore from: outputs/Diffusion_numpy
[05:49:23] - optimizer checkpoint not found
[05:49:23] - model battery_network.0.pth not found

The execution does not proceed after this point. I waited for more than an hour.

This issue is not present when 1D arrays are used for specifying constraints

    cs_ar = np.reshape(np.array([c_s_0 for i in range(N_an*Np_an*1)]), (N_an*Np_an*1,1))
    yp_ar = np.reshape(np.array([c_s_0 for i in range(N_an*Np_an*1)]), (N_an*Np_an*1,1))
    xp_ar = np.reshape(np.array([c_s_0 for i in range(N_an*Np_an*1)]), (N_an*Np_an*1,1))
    tp_ar0 = np.reshape(np.array([0.0 for i in range(N_an*Np_an*1)]), (N_an*Np_an*1,1))

Is it because multidimensional arrays are not supported in modulus constraints?

Hi @shubhamsp2195

That is correct. Modulus looks at training points without any underlying structure. This makes sampling training points from arrays and continuous geometry the same under-the-hood. The first dimension is treated at the number of training points you have, and the second is the dimensionality of that variable (typically 1). So you should always flatten your input arrays to [N,1].

Hi,

I am thinking of using my OpenFOAM / Pointwise generated grids as collocation /training points so that the collocation points can be clustered together better to the wall boundary. So can use the above recommended method to do so?

Thanks.

Based on the above hints, I have tried 2 approaches to using my own points for training:

# load OpenFOAM grid data
        mapping = {
           "Points:0": "x",
           "Points:1": "y",
        }
        openfoam_var = csv_to_dict(
           to_absolute_path("openfoam/FX63-180_xy_uvp_AoA" + str(int(AoA)) + "_skip1.csv"), mapping
        )
        openfoam_invar_numpy = {
           key: value for key, value in openfoam_var.items() if key in ["x", "y"]
        }

# interior
        OF_grid = PointwiseConstraint.from_numpy(
            nodes=nodes,
            #invar=openfoam_invar_numpy,
            
            invar={"x": of_grid_x, "y": of_grid_y},
            outvar={"continuity": 0, "momentum_x": 0, "momentum_y": 0},
            batch_size=cfg.batch_size.OF_grid,
            lambda_weighting={
                "continuity": Symbol("sdf"),
                "momentum_x": Symbol("sdf"),
                "momentum_y": Symbol("sdf"),
            },
        )
        domain.add_constraint(OF_grid, "OF_grid")

Error executing job with overrides:
Traceback (most recent call last):
File “FX63-180_2_element_airfoil2.py”, line 420, in run
OF_grid = PointwiseConstraint.from_numpy(
File “/home/users/nus/tsltaywb/.local/lib/python3.8/site-packages/modulus-22.9-py3.8.egg/modulus/domain/constraint/continuous.py”, line 178, in from_numpy
dataset = DictPointwiseDataset(
File “/home/users/nus/tsltaywb/.local/lib/python3.8/site-packages/modulus-22.9-py3.8.egg/modulus/dataset/continuous.py”, line 35, in init
super().init(invar=invar, outvar=outvar, lambda_weighting=lambda_weighting)
File “/home/users/nus/tsltaywb/.local/lib/python3.8/site-packages/modulus-22.9-py3.8.egg/modulus/dataset/dataset.py”, line 99, in init
self.lambda_weighting = Dataset._to_tensor_dict(lambda_weighting)
File “/home/users/nus/tsltaywb/.local/lib/python3.8/site-packages/modulus-22.9-py3.8.egg/modulus/dataset/dataset.py”, line 49, in _to_tensor_dict
tensor_dict = {
File “/home/users/nus/tsltaywb/.local/lib/python3.8/site-packages/modulus-22.9-py3.8.egg/modulus/dataset/dataset.py”, line 50, in
key: torch.as_tensor(value, dtype=tf_dt, device=device)
File “/home/users/nus/tsltaywb/.local/lib/python3.8/site-packages/sympy/core/expr.py”, line 325, in float
raise TypeError(“can’t convert expression to float”)
TypeError: can’t convert expression to float

of_grid = np.loadtxt("/home/users/nus/tsltaywb/scratch/ai/modulus/myprojects/FX63-180_2_element_airfoil/openfoam/of_grid.csv", delimiter=',', usecols=(0,1))
        of_grid_x = of_grid[:,0:1]
        of_grid_y = of_grid[:,1:2]
        of_grid_n = len(of_grid)

 # interior
        OF_grid = PointwiseConstraint.from_numpy(
            nodes=nodes,
            #invar=openfoam_invar_numpy,
            
            invar={"x": of_grid_x, "y": of_grid_y},
            outvar={'momentum_x': np.zeros(of_grid_n, 1), 'momentum_y': np.zeros(of_grid_n,1)},
            batch_size=cfg.batch_size.OF_grid,
            lambda_weighting={
                "continuity": Symbol("sdf"),
                "momentum_x": Symbol("sdf"),
                "momentum_y": Symbol("sdf"),
            },
        )
        domain.add_constraint(OF_grid, "OF_grid")

Error is:

Error executing job with overrides:
Traceback (most recent call last):
File “FX63-180_2_element_airfoil2.py”, line 424, in run
outvar={‘momentum_x’: np.zeros(of_grid_n, 1), ‘momentum_y’: np.zeros(of_grid_n,1)},
TypeError: Cannot interpret ‘1’ as a data type

Any suggestion on how to solve the problem?

Hello @tsltaywb
As highlighted by @ngeneva , the arrays must be flattened to the shape (N,1) before using them as input and output variables in PointwiseConstraint.from_numpy() function. It seems like the arrays “of_grid_x” and “of_grid_y” have a different shape altogether.

1 Like

Hi @shubhamsp2195,

Thanks for the reply. What I did is that I loaded my OpenFOAM grid, then I separate them into the x and y coordinates (of_grid_x and of_grid_y).

Their shape are:

of_grid_x.shape = (92260, 1)
of_grid_y.shape = (92260, 1)

So doesn’t it mean that they’ve been flattened to the shape (N,1)?

Hi @tsltaywb

The error listed in your post above TypeError: Cannot interpret ‘1’ as a data type looks like a Numpy API error. I believe your use of np.zeros is incorrect. Unlike PyTorch the shape needs to be in a tuple.

E.g. np.zeros((of_grid_n,1)) not np.zeros(of_grid_n,1)

Thanks @ngeneva I made the change and now got the error:

raise TypeError(“can’t convert expression to float”)
TypeError: can’t convert expression to float

In the end, I managed to get rid of it by removing:

lambda_weighting={
                "continuity": Symbol("sdf"),
                "momentum_x": Symbol("sdf"),
                "momentum_y": Symbol("sdf"),
            },

I wonder why I had to remove it . Will it affect the accuracy of the training?

Also, is there any way to check that the grid has been correctly used?