How to easily use two different .cu files in a main function

In a main function, I want to do a second ray tracing, but the code logic is different from the first ray tracing.My idea is to recreate a module to import a different .cu file and create a new acceleration structure.
I wonder if there is a more convenient way?

Let’s address this from the required OptiX API functions from bottom to top.

If you want to call OptiX with two different ray tracing algorithms from your main host function, then you need to call optixLaunch twice.
https://raytracing-docs.nvidia.com/optix8/guide/index.html#ray_generation_launches#ray-generation-launches

The individual function arguments to the optixLaunch call define what of your OptiX device code runs on what data structures.

The pipeline contains all device programs which are used inside the Shader Binding Table (SBT).
The programs inside the pipeline are defined by an array of OptixProgramGroupDesc which define the OptixModule, program domain type, and program name.

The OptixModule are created from input PTX or OptiX-IR code containing your OptiX device programs.
You can have as many OptixModules as you like, or you can put all device programs in one module, but since that is bigger, the optixModuleCreate can get slower.
I’m usually splitting modules by domain type:
https://github.com/NVIDIA/OptiX_Apps/blob/master/apps/MDL_renderer/src/Device.cpp#L344
https://github.com/NVIDIA/OptiX_Apps/blob/master/apps/MDL_renderer/src/Device.cpp#L713

The SBT defines which of the pipeline programs are called when during the optixLaunch.
While you can have multiple ray generation programs in one module and in one pipeline, you can only use one of them inside an optixLaunch because there is only one ray generation program inside an SBT! That defines the entry point of the launch.

The pipelineParams and pipelineParamsSize define which data get copied to your constant launch parameter structure which name you gave to OptiX in OptixPipelineCompileOptions::pipelineLaunchParamsVariableName

Which acceleration structures are used, is defined by the OptixTraversableHandle argument inside the optixTrace call.
The OptixTraversableHandle(s) to use is usually stored inside the OptiX launch parameter structure.
These handles are generated usually by building instance or geometry acceleration structures with optixAccelBuild or a following optixAccelCompact.

Let’s look at the two extreme cases:

1.) If you want to implement two completely different ray tracing algorithms running on different geometry, you need two pipelines, two SBTs, two launch parameter structures, and two top-level OptixTraversableHandles.
You could share some modules if you wanted, but basically everything is different.

2.) If you only want to implement two different cameras (e.g. perspective, orthographic) which otherwise work on the same scene data and ray tracing algorithm (light transport), then you only need two different ray generation programs and either two different SBTs or you need to exchange the ray generation program inside the SBT between optixLaunch calls.
The better alternative would be to implement both camera models inside a single ray generation program and switch between them with a value inside the launch parameters.
Related thread: https://forums.developer.nvidia.com/t/multiple-raygen-functions-within-same-pipeline-in-optix-7/122305/2

Now depending on what you really need, there are multiple different implementation options in between these two which can reuse some amount of data.

I get it.Thank you very much for your advice