flaport / fdtd

A 3D electromagnetic FDTD simulator written in Python with optional GPU support
https://fdtd.readthedocs.io
MIT License
454 stars 116 forks source link

ffmpeg not installed? #54

Open kerstin921 opened 1 year ago

kerstin921 commented 1 year ago

None of your example files will generate a video on Windows.

The error ffmpeg not installed? is thrown despite ffmpeg-python being installed.

I looked up the relevant function generate_video() in your source code and it says Note: this function requires ``ffmpeg`` to be available in your path., so I've tried copying the ffmpeg package folder into the same folder as the .ipynb notebook containing your example simulations and even moving all .py files directly into it, but to no avail.

Could you please check what's wrong and, if necessary, make corrections so that fdtd will recognize the ffmpeg package?

flaport commented 1 year ago

If you want to use ffmpeg to export a video, you need the ffmpeg executable (not the python library) in your path. You can download ffmpeg here.

kerstin921 commented 1 year ago

Thanks, but it still doesn't work. I put ffmpeg.exe into the same folder as the Jupyter Notebook file with your example scripts, but I still get ffmpeg not installed?. Could it maybe be that your scripts change the cwd?

kerstin921 commented 1 year ago

...indeed: For instance, your GRIN example script changes cwd to the subfolder \\fdtd_output\\fdtd_output_2022-7-15-17-22-58 (GRIN)\\fdtd_output\\fdtd_output_2022-7-15-17-28-17 (GRIN). How to remediate?

It certainly doesn't make sense to be forced to put a copy of ffmpeg to each and every output folder, especially since they are dynamically created and named on runtime. So at the present state, these scripts are impossible to function properly, at least the final video rendering step!

Also, one cannot simply change cwd to the script path since then your grid.generate_video() won't find the video frames to work on. This seems like a problem that needs to be solved on source code level.

PS.: You should re-open this issue until it is solved.

kerstin921 commented 1 year ago

Yip, just checked. Adding os.chdir() in front of your animation code block and setting cwd to the notebook path breaks your grid.generate_video()

kerstin921 commented 1 year ago

HELLO???

flaport commented 1 year ago

Hi @kerstin921 , This is an open source project. We're all developing this in our free time. I am sorry for not replying within 24 hours. It seems you found a bug in the video exporting feature, and from what I can tell you're already half way there in understanding the problem and fixing it... So if it's not too much to ask, please fix it, create a PR and it will get accepted. This is how open source works.

kerstin921 commented 1 year ago

Hi flaport,

thanks for this information. I was under the impression that this basically is Python's official FDTD package, which is why I was so confused about the existence of such bugs (meaning it should've been reported earlier as people simply cannot miss it running your example scripts).

What is a PR and how does it solve the issue?

kerstin921 commented 1 year ago

Yeah...no.

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Input In [28], in <cell line: 9>()
      7 grid[:, 0:10, :] = fdtd.PML(name="pml_ylow")
      8 grid[:, -10:, :] = fdtd.PML(name="pml_yhigh")
----> 9 simfolder = grid.save_simulation("test")  # initializing environment to save simulation data
     10 print(simfolder)
     13 # Source

File C:\Program Files\Python38\lib\site-packages\fdtd\grid.py:433, in Grid.save_simulation(self, sim_name)
    431 # storing timestamp title for self.generate_video
    432 self.full_sim_name = full_sim_name
--> 433 makedirs(self.folder, exist_ok=True)
    434 return self.folder

File C:\Program Files\Python38\lib\os.py:223, in makedirs(name, mode, exist_ok)
    221         return
    222 try:
--> 223     mkdir(name, mode)
    224 except OSError:
    225     # Cannot rely on checking for EEXIST, since the operating system
    226     # could give priority to other errors like EACCES or EROFS
    227     if not exist_ok or not path.isdir(name):

FileNotFoundError: [WinError 206] The filename or extension is too long: 'C:\\Users\\Username\\Desktop\\coherence_visualization\\fdtd_output\\fdtd_output_2022-7-22-19-43-46 (test)\\fdtd_output\\fdtd_output_2022-7-22-19-46-1 (test)\\fdtd_output\\fdtd_output_2022-7-22-19-46-53 (test)\\fdtd_output\\fdtd_output_2022-7-23-18-39-49 (test)'

This package is nowhere near production-ready and already breaks at the very first steps - so I'm out. Not worth the trouble and time investment only to find more bugs along the road.

gprmax is the way to go.

flaport commented 1 year ago

Thanks for the hint @kerstin921 , gprmax seems indeed pretty cool!

kerstin921 commented 1 year ago

It is, they used gprmax to design the ground-penetrating radar of the Perseverance Mars mission

kerstin921 commented 1 year ago

@flaport Just to illustrate how bad the problem is, consider this baby example (even much simpler than your examples):

wavelength = 1e-6

# Initialize FDTD grid
grid = fdtd.Grid(shape=(20*wavelength, 50*wavelength, 1), grid_spacing=wavelength/10)

# Initialize perfectly matched layer (PML) boundaries
grid[0:10, :, :] = fdtd.PML(name="pml_xlow")
grid[-10:, :, :] = fdtd.PML(name="pml_xhigh")
grid[:, 0:10, :] = fdtd.PML(name="pml_ylow")
grid[:, -10:, :] = fdtd.PML(name="pml_yhigh")

# Initialize source
grid[:, 10, 0] = fdtd.LineSource(period=wavelength/299792458, name="source")

# Initialize objects
grid[10:190, 50:100, :] = fdtd.AbsorbingObject(permittivity=2.5, conductivity=1e-6, name="absorbing_object")

# Run simulation
start_time = timeit.default_timer()

for i in range(100):
    grid.step()  # Run simulation one timestep at a time and animate
    if i % 5 == 0:
        grid.visualize(z=0, cmap='plasma', animate=True, index=i, save=False)
        plt.title(f"{i}")
        clear_output(wait=True)  # Only necessary in Jupyter notebooks

print(timeit.default_timer() - start_time)

The output:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [51], in <cell line: 17>()
     13 grid[:, 10, 0] = fdtd.LineSource(period=wavelength/299792458, name="source")
     16 # Initialize objects
---> 17 grid[10:190, 50:100, :] = fdtd.AbsorbingObject(permittivity=2.5, conductivity=1e-6, name="absorbing_object")
     20 # Run simulation
     21 start_time = timeit.default_timer()

File C:\Program Files\Python38\lib\site-packages\fdtd\grid.py:365, in Grid.__setitem__(self, key, attr)
    362 else:
    363     raise KeyError("maximum number of indices for the grid is 3")
--> 365 attr._register_grid(
    366     grid=self,
    367     x=self._handle_single_key(x),
    368     y=self._handle_single_key(y),
    369     z=self._handle_single_key(z),
    370 )

File C:\Program Files\Python38\lib\site-packages\fdtd\objects.py:193, in AbsorbingObject._register_grid(self, grid, x, y, z)
    187     conductivity = conductivity[..., None]
    188 self.conductivity = bd.broadcast_to(
    189     conductivity, self.inverse_permittivity.shape
    190 )
    192 self.absorption_factor = (
--> 193     0.5
    194     * self.grid.courant_number
    195     * self.inverse_permittivity
    196     * self.conductivity
    197     * self.grid.grid_spacing
    198     * const.eta0
    199 )

RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!

And this perfectly illustrates the problem with Python: People putting hundreds of non-functional packages into the official PyPI repository, tricking users to waste endless hours into experimenting with packages that don't even pass the simplest test cases, then waving hands and saying "It's open-course!" and "Community!".

While in reality, they just want to have a GitHub repository to pad their CVs and couldn't care less about quality.

flaport commented 1 year ago

Hi @kerstin921 ,

I am sorry you had a bad experience with this package. Of course having non-functional code in the library must be very frustrating for you.

I know the library has not been actively maintained recently and I have probably been accepting pull requests too easily in the past, which made parts of the code unstable (see for example #15, which introduced the video generation function).

That said, I will do a thorough review of the library in the near future (when I find the time) to find and remove bugs and to make sure it's back to a stable state.

Please do understand that I have a full time job which means I don't have the time to work on and maintain the projects like I used to. I am sorry this lead to lost time for you.

kerstin921 commented 1 year ago

It's not 100% lost time (maybe 80% or so) as I'm still familiarizing and learning a few things while trying to get your package working. The problem is that the very basic examples seem not to be working. Usually, such problems are quickly resolved (since examples normally give a working demonstration) because else you'd bascially be dealing with a "cool", but non-functioning codebase published for everyone on the internet. And some of the files/many functions contain almost no documentation, so even for willing beta testers like me it quickly gets super frustrating to fix anything (and I'm not getting paid). Sorry to stress you out

kerstin921 commented 1 year ago

But maybe that's just me as I use to write code as if meant for kindergardeners (i.e. every little piece is documented), so I can basically just give it to a collegue and there will hardly ever come back questions

herm60 commented 1 year ago

png can be merged with ffmpeg using the following DOS commands after installation, but each file shall be parsed correctly : (ex: file0001.png, file0002.png...file0500.png)

ffmpeg -framerate 10 -start_number 0 -i file%4d.png -c:v libx264 -pix_fmt yuv420p out.mp4

files by 10 steps

(ex: file0010.png, file0020.png...file5000.png)

ffmpeg -framerate 10 -start_number 0 -i file%4d.png -c:v libx264 -pix_fmt yuv420p out.mp4

example of storing files by single entries:

simfolder = grid.save_simulation("Emtest") # initializing environment to save simulation data print(simfolder)

with open(os.path.join(simfolder, "grid.txt"), "w") as f: f.write(str(grid)) wavelength = 3e8/grid.source.frequency wavelengthUnits = wavelength/grid.grid_spacing GD = np.array([grid.x, grid.y, None])

for i in range(500): grid.step() # running simulation 1 timestep a time and animating

saving frames during visualization with index 1

        grid.visualize(z=0, animate=True, index=i, save=True, folder=simfolder)
        plt.title(f"{i:3.0f}")