Skip to content

Conversation

@marc-flex
Copy link
Contributor

@marc-flex marc-flex commented Nov 21, 2025

This new version of the Thermo-optic doped modulator unifies the existing Charge and Heat simulation into a single non-isothermal Charge simulation. As a result, the notebook is significantly simplified.

To ease convergence of the thermal charge case, the doping distribution has been changed slightly. It now uses gaussian boxes so that the transition between p and p+ regions goes a bit more smoothly, which helps convergence and reduces the very high local electric field in those transitions.

Additional plots have been added to directly visualize carrier and temperature distributions evolution with applied voltage

@github-actions
Copy link
Contributor

github-actions bot commented Nov 21, 2025

Spell Check Report

ThermoOpticDopedModulator.ipynb:

Cell 12, Line 1: 'Heat-Charge'
  > ## Heat-Charge simulation
Cell 17, Line 3: 'Heat-Charge'
  > We create now all the structures used in both the optic and Heat-Charge simulations.
Cell 23, Line 1: 'non-isothermal'
  > Let's also inspect the doping profiles and make sure it is what we were expecting. Note the smooth transition of doping between the p and p+ regions. These smooth transitions avoid sharp variations in electric potential hence avoiding artificially high electric field. This is relevant in non-isothermal simulations since some of the heat source terms are proportional to the electric field.
Cell 25, Line 2: 'Heat-Charge'
  > Since we're interested in the response of the system for different applied voltages we'll need to solve the Heat-Charge problem at each of these voltages.
Cell 33, Line 1: 'Heat-Charge'
  > ### Heat-Charge simulation object
Cell 33, Line 4: 'SteadyChargeDCAnalysis'
  > In the current case, we are going to run a **non isothermal DC** case which we can define the simulation with `SteadyChargeDCAnalysis`. In DC mode we can set the parameter `convergence_dv` which tells the solver to limit the size of the sweep, i.e., if we need to solve for a bias of 0 and 0.5 and we set `convergence_dv=0.1`, it will force the solver to go between 0 and 0.5 at intervals of 0.1.
Cell 53, Line 8: 'NedeljkovicSorefMashanovich'
  > For the Si charge-induced variation, we will use the empirical relationships presented in `M. Nedeljkovic, R. Soref and G. Z. Mashanovich, "Free-Carrier Electrorefraction and Electroabsorption Modulation Predictions for Silicon Over the 1–14- μm Infrared Wavelength Range," IEEE Photonics Journal, vol. 3, no. 6, pp. 1171-1180, Dec. 2011 ` which has been implemented in the class `NedeljkovicSorefMashanovich`.

Checked 1 notebook(s). Found spelling errors in 1 file(s).
Generated by GitHub Action run: https://github.com/flexcompute/tidy3d-notebooks/actions/runs/19682978685

@marc-flex marc-flex self-assigned this Nov 21, 2025
@marc-flex marc-flex marked this pull request as ready for review November 21, 2025 09:43
@momchil-flex
Copy link
Collaborator

Thanks! Could you change the base branch to pre/2.10 as this is using features that are not yet released. I only got time for a first pass but here's some comments:

  • "Next we'll use this doping to the dine the semiconductor properties." -> to define
  • "Note the smooth transition of doping between the p and p+ regions." -> since you point in the comment in this PR that this was important for convergence, I would also add a sentence about this in the notebook.
  • "set an relative tolerance" -> set a relative tolerance
  • Not your fault but I notice some of the documentation links are pointing to v2.8.0rc2. Could you update them all to point to latest instead. Also could you add a link to DistanceUnstructuredGrid where you discuss it so the users can get more information about the settings.
  • A few things about this plot:
image
  1. Maybe if you plot at z slightly different from 0, the monitor won't show so the yellow overlay will disappear? It's a bit distracting.
  2. why is the bottom plot shaded with diagonal lines going in both directions?
  3. It's also making me realize that the non-isothermal computation requires a bigger domain than the charge computation alone would. Do you think that while gaining in convenience, one could actually get worse performance because of this, compared to doing separate heat and charge?
  • There's sooo many warnings! :|

@FilipeFcp
Copy link
Contributor

FilipeFcp commented Nov 21, 2025

That is great, Marc, thanks!

Just to double-check, the simulations are independent, not coupled? It solves for the current and then uses that current to calculate the heat, is that correct?

At cell [18], I think these lines can be removed:

# size=(2 * x_total, h_core, 0),
# center=(0, h_core / 2, 0),

This is totally optional, but I think it would be better to create an instance of the batch object, in case the user needs to abort the simulation:

mode_datas = web.Batch(simulations=mode_solvers, reduce_simulation=True).run()

@FilipeFcp
Copy link
Contributor

Also, is it possible to have an API function to wrap the interpolation at cell [31]. Something like this:

    perturbed_sims = []
    target_grid = optic_sim.grid.boundaries
    voltages = charge_monitor.electrons.values.voltage

    for n, v in enumerate(voltages):
        # deal first with temperature field
        temp_interpolated = (
            heat_monitor
            .temperature.sel(voltage=v)
            .interp(x=target_grid.x, y=target_grid.y, z=0, fill_value=Tref)
        )
        coords_charge = {
            "x": temp_interpolated.coords.get("x").values,
            "y": temp_interpolated.coords.get("y").values,
            "z": temp_interpolated.coords.get("z").values,
        }
        # convert to SpatialDataArray
        temp_interpolated = td.SpatialDataArray(
            np.squeeze(temp_interpolated.values, axis=3), coords=coords_charge
        )

        # now deal with charge distributions
        e_interpolated = (
            charge_monitor
            .electrons.sel(voltage=v)
            .interp(x=target_grid.x, y=target_grid.y, z=0, fill_value=0)
        )
        h_interpolated = (
            charge_monitor
            .holes.sel(voltage=v)
            .interp(x=target_grid.x, y=target_grid.y, z=0, fill_value=0)
        )
        # convert to SpatialDataArray
        e_interpolated = td.SpatialDataArray(
            np.squeeze(e_interpolated.values, axis=3), coords=coords_charge
        )
        h_interpolated = td.SpatialDataArray(
            np.squeeze(h_interpolated.values, axis=3), coords=coords_charge
        )

        psim = optic_sim.perturbed_mediums_copy(
            electron_density=e_interpolated, hole_density=h_interpolated, temperature=temp_interpolated
        )

        perturbed_sims.append(psim)

So for the user side, it just needs to call:

perturbed_sims = create_perturbed_sims(optic_sim, charge_data["charge_mnt"],charge_data["temperature"])

If something like that is possible, I think it would create a cleaner workflow for demos and also highlight the integration of our solvers, which adds significant value.

@marc-flex marc-flex force-pushed the marc/new_thermooptic_doped_modulator branch from 51eaab1 to 2656c5e Compare November 25, 2025 09:41
@marc-flex
Copy link
Contributor Author

@FilipeFcp that piece of code was outdated. perturbed_medium_copy now accepts unstructured data

@FilipeFcp
Copy link
Contributor

Thanks @marc-flex!
It looks really neat!

@marc-flex
Copy link
Contributor Author

@momchil-flex I have addressed your comments. Only this is missing:

Maybe if you plot at z slightly different from 0, the monitor won't show so the yellow overlay will disappear? It's a bit distracting.

Monitors have infinite sizes so they'll always be there regardless of where the z is defined?

why is the bottom plot shaded with diagonal lines going in both directions?

I need to investigate that actually. It shouldn't be too different from the heat plot.

It's also making me realize that the non-isothermal computation requires a bigger domain than the charge computation alone would. Do you think that while gaining in convenience, one could actually get worse performance because of this, compared to doing separate heat and charge?

I'd say the increased cost is probably small since the mesh is coarser outside the semiconductor. Plus we're only meshing

@momchil-flex
Copy link
Collaborator

@momchil-flex I have addressed your comments.

Thanks!

Maybe if you plot at z slightly different from 0, the monitor won't show so the yellow overlay will disappear? It's a bit distracting.

Monitors have infinite sizes so they'll always be there regardless of where the z is defined?

Ah, in FDTD sims we often make the monitor 0D along the 0 dimension, which is what I was imagining here but it's not the case. I am not sure if doing this will cause some issues with e.g. numerical precision but maybe good to know/try?

It's also making me realize that the non-isothermal computation requires a bigger domain than the charge computation alone would. Do you think that while gaining in convenience, one could actually get worse performance because of this, compared to doing separate heat and charge?

I'd say the increased cost is probably small since the mesh is coarser outside the semiconductor. Plus we're only meshing

Great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants