LupoLab / Luna.jl

Nonlinear optical pulse propagator
MIT License
65 stars 27 forks source link

How to use Custom Temporal Field for Freespace Propagation #357

Closed CaptainKubi closed 5 days ago

CaptainKubi commented 7 months ago

I want to perform a simulation like the one shown in the example radial.jl code for freespace simulation; however, using a custom time domain field. We are particularly interested in simulating the nonlinear response in a two-color experiment where two short pulses with different colors are delayed with respect to each other. The two pulse shapes can not be described easily by any predefined envelope function. However, we have an experimental characterization of the electric field, which we would like to utilize for the simulations.

All the examples that I found use simply the GaussGaussField function. However, only being able to perform calculations for generic Gaussian pulses severely reduces the usefulness for us.

I have already encountered this problem multiple times, and in the past, this was the reason I did not continue using Luna. jl for my work. I want to give it a go once again.

The Luna.run() command requires a NonlinearRHS struct as input, which is generated in the examples from the Luna.setup() function. This function, however, requires a SpatioTemporalField struct. These structs, in turn, always seem to consist of simple pulses described by a few parameters, such as central wavelength, energy, pulse length, and envelope shape.

I found the DataField struct. However, this only creates a time-domain pulse, and I did not find a way to add the spatial degrees of freedom, e. g. a Gaussian beam shape, to it, so I can call it the Luna.setup() function.

I would very much appreciate your help in this matter.

Maybe adding another example that shows how the DataField struct can actually be used might also help others.

chrisbrahms commented 7 months ago

The short answer is that this simply hasn't been implemented. A SpatioTemporal is indeed only intended for simple shapes like Gaussians, and this is all we've ever had cause to use so far. Adding more flexibility here (e.g. by composing TimeFields with a spatial distribution) is on our long to-do list for Luna, but the good news is that you don't have wait for us to get there. You can simply leave the inputs argument empty and do whatever you like to create your own input field.

inputs = () # empty tuple
_, transform, FT = Luna.setup(grid, q, densityfun, normfun, responses, inputs)

Here I'm just throwing away the output from Luna.setup (which is all zeros anyway). Now I can create my own, in this case using a Gaussian spatial distribution and a DataField:

# dummy data--replace this with your measured data in the format required for DataField
ωdat = collect(range(0, 5e15, 512))
Eωdat = complex(Maths.gauss.(ωdat.-2.4e15; fwhm=1e14))

df = Fields.DataField(ωdat, Eωdat; energy=1) # energy is irrelevant here as we will rescale again
Eω = df(grid, nothing) # second argument is unused for DataField (need the planned FT for other types)

Eωr = Eω .* sqrt.(Maths.gauss.(q.r, w0/2))' # combine with spatial distribution

Eωk = q * Eωr # transform from ω-r space to ω-k space
Eωk .*= sqrt(energy)/sqrt(energyfun_ω(Eωk)) # rescale to actual energy

Fields.prop!(Eωk, -0.3, grid, q)

output = Output.MemoryOutput(0, grid.zmax, 201)
Luna.run(Eωk, grid, linop, transform, FT, output)

This is essentially pulling out some of the internals from Fields.SpatioTemporalField to hack together my own version of a spatial DataField. Hopefully this will get you started, but of course feel free to ask more questions if this doesn't work.