Tidy3D Workspace: Blender Plugin #2

Closed
opened 2024-01-29 16:17:28 +01:00 by so-rose · 4 comments

Requirements

The goal of this project is to use computational techniques to identify methods of detecting and sorting chiral molecules. The process of this goal inevitably involves performing "numerical experiments", where a suitable 3D Maxwell solver is prepared, run, and analyzed, in order to guide the convergence of theoretical intuitions.

This process is not simply empirical: The effects that are being searched for are nuanced, and will most likely require intuitive reasoning about simulation outputs before effective empirical techniques can find very much success.

Therefore, it is critical that a suitable 3D workspace be chosen. The following are the ideal requirements placed on such a tool:

  • Defining the simulation domain in 3D space and easily attaching simulation parameters.
  • Defining simulation monitors and easily attaching monitoring parameters.
  • Defining arbitrarily complex boundary conditions as triangle meshes / voxel fields, which might include attributes representing ex. mechanical strength.
  • Visualizing 3D time-domain simulation outputs in real-time, including float and vec3 voxel fields, with the ability to:
    • Quickly define arbitrary visual post-processing such as custom color mapping and "slicing", to highlight key contrasts within fields, interactions between fields, and behavior along critical 2D planes.
    • Simultaneously visualize several angles and planes in real-time, to better understand the whole simulation.
    • Overlay the geometric result of an inverse design optimization onto the fields it creates, using tools to quickly adjust the rendering methods for appropriate visual contrast.
  • Providing for high-quality, non real-time 3D visualization of any interesting results.

Existing Tools

For input visualization, no tools really fit the bill.

  • Tidy3D does have a method of defining a simulation in a web interface, but reproducibility and performance are of concern.

Tidy3D ships with several functions for certain kinds of output visualization.

  • Internally, xarray is used to represent data output from monitors.
  • For 2D plotting, Tidy3D relies on matplotlib. Though convenience functions are provided, manual analysis with other plotting libraries is more desirable.
  • For 3D plotting, Tidy3D provides a web interface. Its quality is decent, but again, performance is a concern, as well as reliance on a particular infrastructure.

Usually, favorite plotting libraries like plotly, jupyter-k3d, etc. do the trick - however, they chronically suffer from:

  • Lack of 3D tools, especially for voxels.
  • Lack of ability to animate, and if so, not in real time.

These libraries remain appropriate for 2D plotting, even animated (ideally through ex. the holoviews interface); however, for realtime configurable visualization of 3D voxel structures, they are insufficient.

Proposed Tool

Blender (https://www.blender.org/) is a tool traditionally used for non-scientific 3D work. However, out of the box, it actually manages to come extremely close:

  • Object system including triangle-meshes, 3D float/vector voxel grids in the "OpenVDB" format, "empty" spatial (coordinate) information, or fully parametric (for ex. mathematically "perfect" shapes), all supporting custom attached data parameters.
  • Animation system optimized for real-time visualization of "moving" data, including 3D voxel data.
  • Rapid tools for shading and geometric manipulation, including "slicing", mirroring, and coloring of fields.
  • Extremely flexible Python-based plugin system, with easy access to custom UI elements, and the ability to directly integrate other Python-based tools for tailoring Blender to particular needs.

Therefore, the proposed approach is to tailor Blender to meet our requirements for a tool.

Proposed Workflow

The goal is to provide the smallest possible well-designed glue software that provides for the following proposed workflow:

  • The user designs the simulation in Blender, using appropriate ex. objects.
  • When satisfied, the user uses the library to load simulation info from Blender, save the setup (for reproducibility), run the simulation, and write results back to Blender.
  • The user performs visual analysis of the results in both the notebook (simpler plots and 2D), and in Blender. This might guide the user to:
    • Investigate a particular element of the simulation empirically, in the notebook.
    • Adjusting the simulation, which might be loaded into Blender to check the setup before re-running.
    • Designing a new simulation, again in Blender.

Blender (w/Plugin) should be run on a sufficiently capable workstation, which is network-accessible to a computer on which the user is working. The user computer does the scientific work in a ex. Notebook, which does not require a capable workstation. These can be the same computer, but don't have to be.

Component: Blender Addon

The addon fundamentally has three functions:

  • Defining simulations using object properties.
  • Responding to web queries requesting information about the defined simulation.
  • Responding to web queries for visualization of finished simulation data by setting up appropriate Blender objects.

This is to keep the Blender-specialized code to a minimum, as well as allowing the use of a more powerful networked workstation for the heavier visualizations required.
To define simulations, the following conventions are used.

The plugin should listen for queries for this internal data, and respond appropriately.

To visualize, the plugin should:

  • 3D Monitor
    • Receive voxel data as a path to a local OpenVDB file sequence.
    • Create a Volume datablock pointing to this sequence.
    • Add an appropriate shader preset using colors to visualize ex. charges, velocity,
    • Add GeoNodes preset to quickly customize the display, ex. slicing, examining a boundary cube, etc. .
  • 2D Monitor (slice)
    • Receive path to an image sequence (jpg)
    • Create an Image (sequence) pointing to this sequence.
    • Add a plane with a shader preset that plays the image sequence on top of it.

Standard Blender tools can be used to, for example:

  • Monitor the simulation from multiple cameras.
  • Perform non-standard manipulations on the voxels, to investigate particular properties.
  • Produce high-quality visualizations (static or animated) of any important simulation results.

Python Library

A minimalist companion library, which allows the following:
- Querying the Blender plugin for simulation info, which can be customized/loaded into Tidy3D.
- Syncing simulation setup in the notebook and in Blender.
- Converting Tidy3D xarray data into OpenVDB/jpg for use by Blender.

Procedure

  • Formally define the why and what of this project's simulation visualization strategy.
    New checklist:
  • Make a bash script that quickly packs the addon (as zip), then opens template.blend with a Python script that registers the addon, so that the terminal shows errors.
  • Find a clean way to add PyPi packages w/sys.path hacking, via an operator (also accessible via the "install" button in Preferences). Add tidy3d.
  • Create base data structures to tie to node socket types, which align closely to tidy3d's.
  • Add "compute" functions to all nodes, which update internal properties based on what is needed to satisfy the currently linked output sockets.
  • Construct a Tidy3D simulation object using a manual button on the node graph, when it is "run".
  • Add an "export" node, which takes a file path, and just prints the tidy3d.simulation.json() into the specified file path.
  • Add visualization-oriented output to key nodes (where relevant), and add dedicated "viz" nodes.
  • Design a few geonodes trees, and export them to Python operators using https://github.com/BrendanParmer/NodeToPython
  • Design the viz nodes to use operators to setup hardcoded geonodes trees and hardcoded shader trees, and alter the geonodes modifier group inputs / shader value nodes when the node graph is "run".
  • Hookup a "run live" event, which pings on many kinds of events (especially any node tree updates), and which viz nodes in turn respond to when driving geonodes trees.
  • Validate/iterate the plugin structure.

Fixes:

  • Fix the Sellmeier presets, in a way so we can add presets to other things.
  • Make 'BlenderMaxwellInstallDependenciesOperator non-blocking.

At this point, we are ready to incrementally add features to the plugin/library as we need them.

# Requirements The goal of this project is to use computational techniques to identify methods of detecting and sorting chiral molecules. The process of this goal inevitably involves performing "numerical experiments", where a suitable 3D Maxwell solver is prepared, run, and analyzed, in order to guide the convergence of theoretical intuitions. This process is not simply empirical: The effects that are being searched for are nuanced, and will most likely require intuitive reasoning about simulation outputs before effective empirical techniques can find very much success. Therefore, it is critical that a suitable 3D workspace be chosen. The following are the ideal requirements placed on such a tool: - Defining the **simulation domain** in 3D space and easily attaching simulation parameters. - Defining **simulation monitors** and easily attaching monitoring parameters. - Defining **arbitrarily complex boundary conditions** as triangle meshes / voxel fields, which might include attributes representing ex. mechanical strength. - Visualizing 3D time-domain simulation outputs in **real-time**, including float and vec3 voxel fields, with the ability to: - Quickly define **arbitrary visual post-processing** such as custom color mapping and "slicing", to highlight key contrasts within fields, interactions between fields, and behavior along critical 2D planes. - Simultaneously visualize several angles and planes in real-time, to better understand the whole simulation. - Overlay the geometric result of an inverse design optimization onto the fields it creates, using tools to quickly adjust the rendering methods for appropriate visual contrast. - Providing for high-quality, non real-time 3D visualization of any interesting results. # Existing Tools For input visualization, no tools really fit the bill. - Tidy3D does have a method of defining a simulation in a web interface, but reproducibility and performance are of concern. Tidy3D ships with several functions for certain kinds of output visualization. - Internally, `xarray` is used to represent data output from monitors. - For 2D plotting, Tidy3D relies on `matplotlib`. Though convenience functions are provided, manual analysis with other plotting libraries is more desirable. - For 3D plotting, Tidy3D provides a web interface. Its quality is decent, but again, performance is a concern, as well as reliance on a particular infrastructure. Usually, favorite plotting libraries like `plotly`, `jupyter-k3d`, etc. do the trick - however, they chronically suffer from: - Lack of 3D tools, especially for voxels. - Lack of ability to animate, and if so, not in real time. These libraries remain appropriate for 2D plotting, even animated (ideally through ex. the `holoviews` interface); however, for realtime configurable visualization of 3D voxel structures, they are insufficient. # Proposed Tool Blender (<https://www.blender.org/>) is a tool traditionally used for non-scientific 3D work. However, out of the box, it actually manages to come extremely close: - Object system including triangle-meshes, 3D float/vector voxel grids in the "OpenVDB" format, "empty" spatial (coordinate) information, or fully parametric (for ex. mathematically "perfect" shapes), all supporting custom attached data parameters. - Animation system optimized for real-time visualization of "moving" data, including 3D voxel data. - Rapid tools for shading and geometric manipulation, including "slicing", mirroring, and coloring of fields. - Extremely flexible Python-based plugin system, with easy access to custom UI elements, and the ability to directly integrate other Python-based tools for tailoring Blender to particular needs. Therefore, the proposed approach is to tailor Blender to meet our requirements for a tool. ## Proposed Workflow The goal is to provide the smallest possible well-designed glue software that provides for the following proposed workflow: - The user designs the simulation in Blender, using appropriate ex. objects. - When satisfied, the user uses the library to load simulation info from Blender, save the setup (for reproducibility), run the simulation, and write results back to Blender. - The user performs visual analysis of the results in both the notebook (simpler plots and 2D), and in Blender. This might guide the user to: - Investigate a particular element of the simulation empirically, in the notebook. - Adjusting the simulation, which might be loaded into Blender to check the setup before re-running. - Designing a new simulation, again in Blender. Blender (w/Plugin) should be run on a sufficiently capable workstation, which is network-accessible to a computer on which the user is working. The user computer does the scientific work in a ex. Notebook, which does not require a capable workstation. These can be the same computer, but don't have to be. ## Component: Blender Addon The addon fundamentally has three functions: - Defining simulations using object properties. - Responding to web queries requesting information about the defined simulation. - Responding to web queries for visualization of finished simulation data by setting up appropriate Blender objects. This is to keep the Blender-specialized code to a minimum, as well as allowing the use of a more powerful networked workstation for the heavier visualizations required. To define simulations, the following conventions are used. - **Simulation Domain**: The parent of the entire simulation. - **Type**: `tidy3d.Simulation` <https://docs.flexcompute.com/projects/tidy3d/en/latest/_autosummary/tidy3d.Simulation.html>. - **Shape**: Properties should define a shape compatible with a `tidy3d.BoundarySpec` (<https://docs.flexcompute.com/projects/tidy3d/en/latest/_autosummary/tidy3d.BoundarySpec.html#tidy3d.BoundarySpec>), which should be represented in Blender. Ideally, the grid that will be used should be shown as a wireframe. - **Source**: Create eletromagnetic excitations. - **Type**: Something from `tidy3d.components.source`; see `tidy3d.Simulation` documentation. - **Shape**: Should match the nature of the source, ex. `PointDipole` or `PlaneWave`. - **Structure**: Any structures interacting with the simulation. - **Type**: `tidy3d.Structure` (<https://docs.flexcompute.com/projects/tidy3d/en/latest/_autosummary/tidy3d.Structure.html#tidy3d.Structure>) - **Shape**: One of the valid structures, including `TriMesh`. In the case of Triangle Mesh, the object should be left alone; else, it should be represented with the specified shape. - **Monitor**: Provide an opportunity for visualizable output. - **Type**: Something from `tidy3d.components.monitors`; see `tidy3d.Simulation` documentation. - **Shape**: One of the valid structures, including `TriMesh`. In the case of Triangle Mesh, the object should be left alone; else, it should be represented with the specified shape. The plugin should listen for queries for this internal data, and respond appropriately. To visualize, the plugin should: - 3D Monitor - Receive voxel data as a path to a local OpenVDB file sequence. - Create a `Volume` datablock pointing to this sequence. - Add an appropriate shader preset using colors to visualize ex. charges, velocity, - Add GeoNodes preset to quickly customize the display, ex. slicing, examining a boundary cube, etc. . - 2D Monitor (slice) - Receive path to an image sequence (jpg) - Create an `Image` (sequence) pointing to this sequence. - Add a plane with a shader preset that plays the image sequence on top of it. Standard Blender tools can be used to, for example: - Monitor the simulation from multiple cameras. - Perform non-standard manipulations on the voxels, to investigate particular properties. - Produce high-quality visualizations (static or animated) of any important simulation results. ## Python Library A minimalist companion library, which allows the following: - Querying the Blender plugin for simulation info, which can be customized/loaded into Tidy3D. - Syncing simulation setup in the notebook and in Blender. - Converting Tidy3D `xarray` data into OpenVDB/jpg for use by Blender. # Procedure - [x] Formally define the why and what of this project's simulation visualization strategy. New checklist: - [x] Make a bash script that quickly packs the addon (as zip), then opens template.blend with a Python script that registers the addon, so that the terminal shows errors. - [x] Find a clean way to add PyPi packages w/sys.path hacking, via an operator (also accessible via the "install" button in Preferences). Add tidy3d. - [x] Create base data structures to tie to node socket types, which align closely to tidy3d's. - [x] Add "compute" functions to all nodes, which update internal properties based on what is needed to satisfy the currently linked output sockets. - [x] Construct a Tidy3D simulation object using a manual button on the node graph, when it is "run". - [x] Add an "export" node, which takes a file path, and just prints the tidy3d.simulation.json() into the specified file path. - [x] Add visualization-oriented output to key nodes (where relevant), and add dedicated "viz" nodes. - [x] Design a few geonodes trees, and export them to Python operators using <https://github.com/BrendanParmer/NodeToPython> - [x] Design the viz nodes to use operators to setup hardcoded geonodes trees and hardcoded shader trees, and alter the geonodes modifier group inputs / shader value nodes when the node graph is "run". - [x] Hookup a "run live" event, which pings on many kinds of events (especially any node tree updates), and which viz nodes in turn respond to when driving geonodes trees. - [x] Validate/iterate the plugin structure. Fixes: - [x] Fix the Sellmeier presets, in a way so we can add presets to other things. - [x] Make 'BlenderMaxwellInstallDependenciesOperator non-blocking. At this point, we are ready to incrementally add features to the plugin/library as we need them.
so-rose self-assigned this 2024-01-29 16:17:35 +01:00
Poster
Owner

Successful test of:

  • Loading OpenVDB animated voxels (a public domain smoke simulation in this case), with options for real-time display of density, slices, and "grid types" (in our simulation results this would be EM field values, flux density, computed permittivity/diffraction, etc.).
  • Real-time playback of float fields on a weak machine (the gray image).
  • Semi real-time playback of shaded results (colorful image) with custom color-ramps, which can be defined by "grid type" (with opportunity to choose which are included, or combine several in a visually useful manner).

Notes:

  • It's very pleasant to work in. 3D navigation is intuitive, "collections" allow organizing complex setups, importing ex. molecular geometry is simple enough.
  • Custom "tabs" should probably be defined to hide some of the "business", in terms of the interface, and deprioritize anything that isn't useful for the task at hand.
  • Next step is trying to define a simple plugin that allows defining a barebones, "hello world" tidy3d simulation, and exporting it as some kind of structured object (json?). We should then try to run a simulation defined like this, and get the plugin to automatically visualize the output fields appropriately.
Successful test of: - Loading OpenVDB animated voxels (a public domain smoke simulation in this case), with options for real-time display of density, slices, and "grid types" (in our simulation results this would be EM field values, flux density, computed permittivity/diffraction, etc.). - Real-time playback of float fields on a weak machine (the gray image). - Semi real-time playback of shaded results (colorful image) with custom color-ramps, which can be defined by "grid type" (with opportunity to choose which are included, or combine several in a visually useful manner). Notes: - It's very pleasant to work in. 3D navigation is intuitive, "collections" allow organizing complex setups, importing ex. molecular geometry is simple enough. - Custom "tabs" should probably be defined to hide some of the "business", in terms of the interface, and deprioritize anything that isn't useful for the task at hand. - Next step is trying to define a simple plugin that allows defining a barebones, "hello world" tidy3d simulation, and exporting it as some kind of structured object (`json`?). We should then try to run a simulation defined like this, and get the plugin to automatically visualize the output fields appropriately.
so-rose added the due date 2024-02-01 2024-01-29 16:29:36 +01:00
so-rose added the
visualization
simulation
labels 2024-01-29 16:38:33 +01:00
so-rose added spent time 2024-01-29 16:51:28 +01:00
3 hours 26 minutes
so-rose removed the due date 2024-02-01 2024-01-29 17:12:29 +01:00
so-rose added the due date 2024-02-02 2024-01-29 17:12:35 +01:00
so-rose added this to the Project Bootstrap milestone 2024-01-29 17:41:13 +01:00
so-rose modified the due date from 2024-02-02 to 2024-02-07 2024-02-05 11:38:01 +01:00
so-rose added spent time 2024-02-05 11:38:12 +01:00
15 hours 2 minutes
Poster
Owner

Plugin has been defined, and I've determined that a custom node tree is the appropriate abstraction.

The base issue's "Procedure" has been edited with a new (working) checklist.

Plugin has been defined, and I've determined that a custom node tree is the appropriate abstraction. The base issue's "Procedure" has been edited with a new (working) checklist.
so-rose started working 2024-02-05 12:19:27 +01:00
so-rose stopped working 2024-02-06 12:49:17 +01:00
1 day
so-rose deleted spent time 2024-02-06 12:50:47 +01:00
- 1 day
so-rose added spent time 2024-02-06 12:50:56 +01:00
9 hours
so-rose started working 2024-02-06 12:51:08 +01:00
so-rose stopped working 2024-02-06 20:30:23 +01:00
7 hours 39 minutes
so-rose deleted spent time 2024-02-06 20:30:32 +01:00
- 7 hours 39 minutes
so-rose pinned this 2024-02-06 21:47:43 +01:00
so-rose modified the due date from 2024-02-07 to 2024-02-14 2024-02-07 12:09:02 +01:00
so-rose added spent time 2024-02-13 10:18:19 +01:00
20 hours
so-rose started working 2024-02-13 10:18:24 +01:00
so-rose stopped working 2024-02-14 08:45:46 +01:00
22 hours 27 minutes
Poster
Owner

Overnight time tracking accounts for untracked Saturday work.

Another rearchitecting later and the data model is being more friendly. Node / socket implementations are going well.

Design decisions that are learned:

  • UI belongs in node sockets. It's the ideal tradeoff. It's surprisingly versatile and responsive. Only real exceptions so far are buttons (operators) and configuration dropdowns (ex. presets).
  • Unit system was a very good time investment. It's dangerously snappy and useful.
  • By keeping units out of Blender, we gain the full sympy flexibility. The cost is a few pain points in extracting the dimensions, but we've addressed this (and the sympy model of unit systems is actually quite helpful in its restrictions).
  • What remains is the unit parts that must "talk to" Blender units. Ideally, the user needs to setup the scene to use micrometers and a BU scale turning one Blender unit into one micrometer; similar for mass. This all needs to be accessible; an operator that nags the user, perhaps, if they try to run simulations without this set properly.
    • The unsolveable issue is Blender's time; FDTD sims at nanometer scale often work on 10**-12 scale, while blender bottoms out at 10**-6 ish (RIP float precision). Since playback is frame-oriented, with a max of 2400fps, we need to do something special - the node tree needs to understand Blender seconds as something configurably smaller.
  • Validating data flow is surprisingly hard, and our solution of compatible types and default_value property overrides is quite versatile. Even without error validation, useful error messages do appear to the user.
  • The "preview" toggle is ideal for output sockets too; ideal for fast overview, quickly and responsively getting productive feedback. So far only for complex numbers, but it's a flexible architecture that can quickly be hashed into any socket type.
  • We can indeed dynamically spawn/destroy input sockets on nodes, with a little care - this is ideal for the generic GeoNodes / Script node.
  • Nodetree-oriented collection is ideal for spawning in objects in an organized way, without resorting to custom gpu viewport drawing code.
  • I'd really like the tree to be more responsive without explicit button pressing. But this wasn't obviously egregiously simple, so it'll wait.
  • Gizmos aren't at all a dumb stretch-goal idea for manipulating node values based on 3D viewport previews.
  • It's apparently extremely easy to write image blocks from custom processes, ex. plotting with matplotlib.
  • "Blender as a Python module" aka. bpy on PyPi is surprisingly light.
  • We should setup an environment with rye (https://rye-up.com/guide/basics/), which in turn can utilize bpy to do things like lint, test, etc. .
  • There is a jax plugin for tidy3d, which allows tidy3d objects that are registered.
  • Node tree caching logic is most definitely a useful line of thinking, if any nodes are ever at all non-performant, but not important now (and "node trees as a closure builder" might get one much further than it has any right to).
  • Scene should have a simulation selector, ex. maybe to control previews? I'm not sure why yet, but it makes sense.
Overnight time tracking accounts for untracked Saturday work. Another rearchitecting later and the data model is being more friendly. Node / socket implementations are going well. Design decisions that are learned: - UI belongs in node sockets. It's the ideal tradeoff. It's surprisingly versatile and responsive. Only real exceptions so far are buttons (operators) and configuration dropdowns (ex. presets). - Unit system was a very good time investment. It's dangerously snappy and useful. - By keeping units out of Blender, we gain the full `sympy` flexibility. The cost is a few pain points in extracting the dimensions, but we've addressed this (and the `sympy` model of unit systems is actually quite helpful in its restrictions). - What remains is the unit parts that must "talk to" Blender units. Ideally, the user needs to setup the scene to use micrometers and a BU scale turning one Blender unit into one micrometer; similar for mass. This all needs to be accessible; an operator that nags the user, perhaps, if they try to run simulations without this set properly. - The unsolveable issue is Blender's time; FDTD sims at nanometer scale often work on 10**-12 scale, while blender bottoms out at 10**-6 ish (RIP float precision). Since playback is frame-oriented, with a max of 2400fps, we need to do something special - the node tree needs to understand Blender seconds as something configurably smaller. - Validating data flow is surprisingly hard, and our solution of compatible types and default_value property overrides is quite versatile. Even without error validation, useful error messages do appear to the user. - The "preview" toggle is ideal for output sockets too; ideal for fast overview, quickly and responsively getting productive feedback. So far only for complex numbers, but it's a flexible architecture that can quickly be hashed into any socket type. - We can indeed dynamically spawn/destroy input sockets on nodes, with a little care - this is ideal for the generic GeoNodes / Script node. - Nodetree-oriented collection is ideal for spawning in objects in an organized way, without resorting to custom `gpu` viewport drawing code. - I'd really like the tree to be more responsive without explicit button pressing. But this wasn't obviously egregiously simple, so it'll wait. - Gizmos aren't at all a dumb stretch-goal idea for manipulating node values based on 3D viewport previews. - It's apparently extremely easy to write image blocks from custom processes, ex. plotting with matplotlib. - "Blender as a Python module" aka. `bpy` on PyPi is surprisingly light. - We should setup an environment with `rye` (<https://rye-up.com/guide/basics/>), which in turn can utilize `bpy` to do things like lint, test, etc. . - There is a `jax` plugin for `tidy3d`, which allows `tidy3d` objects that are registered. - Node tree caching logic is most definitely a useful line of thinking, if any nodes are ever at all non-performant, but not important now (and "node trees as a closure builder" might get one much further than it has any right to). - Scene should have a simulation selector, ex. maybe to control previews? I'm not sure why yet, but it makes sense.
so-rose modified the due date from 2024-02-14 to 2024-02-21 2024-02-21 11:20:26 +01:00
Poster
Owner

I'd consider the plugin to be pseudo-MVP now - there's still a bunch of paper cuts, but it's more appropriate to attach more specialized tracking issues at this point.

Closing.

I'd consider the plugin to be pseudo-MVP now - there's still a bunch of paper cuts, but it's more appropriate to attach more specialized tracking issues at this point. Closing.
Sign in to join this conversation.
There is no content yet.