Note: the cost of running the entire notebook is higher than 1 FlexCredit.
This notebook demonstrates how to set up and run a parameterized level set-based optimization of a Y-branch. In this approach, we use autograd to generate a level set surface \(\phi(\rho)\) given a set of control knots \(\rho\). The permittivity distribution is then obtained implicitly from the zero level set isocontour. Details about the level set method can be found here. Minimum gap and curvature penalty terms are introduced in the optimization to control the minimum feature size, hence improving device fabrication. In addition, we show how to tailor the initial level set function to a starting geometry, which is helpful to further optimize a device obtained by conventional design.
Let’s start by importing the Python libraries used throughout this notebook.
# Standard python imports.import picklefrom typing import List# Import autograd to be able to use automatic differentiation.import autograd.numpy as anpimport gdstkimport matplotlib.pylab as pltimport numpy as np# Import regular tidy3d.import tidy3d as tdimport tidy3d.web as webfrom autograd import gradfrom autograd.tracer import getvalfrom tidy3d.plugins.autograd import adam, apply_updates, optimize, value_and_gradplt.rcParams["font.size"] ="12"
Y-branch Inverse Design Configuration
The y-branch splits the power from an input waveguide into two other output waveguides. Here, we are considering a gap of 0.3 \(\mu m\) between the output waveguides for illustration purposes. However, when considering the design of a practical device, this value can be smaller. S-bends are included to keep the output waveguides apart from each other to prevent mode coupling.
Next, you can set the y-branch geometry and the inverse design parameters.
# Geometric parameters.y_width =1.7# Y-branch maximum width (um).y_length =1.7# Y-branch maximum length (um).w_thick =0.22# Waveguide thickness (um).w_width =0.5# Waveguide width (um).w_length =1.0# Input output waveguide length (um).w_gap =0.3# Gap between the output waveguides (um).bend_length =3# Output waveguide bend length (um).bend_offset =0.5# Offset between output bends (um).# Material.nSi =3.48# Silicon refractive index.# Inverse design set up parameters.grid_size =0.016# Simulation grid size on design region (um).ls_grid_size =0.004# Discretization size of the level set function (um).ls_down_sample = (20# The spacing between the level set control knots is given by ls_grid_size*ls_down_sample.)fom_name_1 ="fom_field1"# Name of the monitor used to compute the objective function.min_feature_size =0.14# Minimum fabrication feature size (um).gap_par =1.0# Parameter to minimum gap fabrication constraint.curve_par =1.5# Parameter of minimum curvature fabrication constraint.# Optimizer parameters.iterations =100# Maximum number of iterations in optimization.learning_rate =0.03# Simulation wavelength.wl =1.55# Central simulation wavelength (um).bw =0.06# Simulation bandwidth (um).n_wl =61# Number of wavelength points within the bandwidth.
From the parameters defined before, a lot of variables are computed and used to set up the optimization.
# Minimum and maximum values for the permittivities.eps_max = nSi**2eps_min =1.0# Material definition.mat_si = td.Medium(permittivity=eps_max) # Waveguide material.# Wavelengths and frequencies.wl_max = wl + bw /2wl_min = wl - bw /2wl_range = np.linspace(wl_min, wl_max, n_wl)freq = td.C_0 / wlfreqs = td.C_0 / wl_rangefreqw =0.5* (freqs[0] - freqs[-1])run_time =8e-13# Computational domain size.pml_spacing =0.6* wlsize_x =2* w_length + y_length + bend_lengthsize_y = w_gap +2* (bend_offset + w_width + pml_spacing)size_z = w_thick +2* pml_spacingeff_inf =10# Source and monitor positions.mon_w =3* w_widthmon_h =5* w_thick# Separation between the level set control knots.rho_size = ls_down_sample * ls_grid_size# Number of points on the parameter grid (rho) and level set grid (phi)nx_rho =int(y_length / rho_size) +1ny_rho =int(y_width / rho_size /2) +1nx_phi =int(y_length / ls_grid_size) +1ny_phi =int(y_width / ls_grid_size /2) +1npar = nx_rho * ny_rhony_rho *=2ny_phi *=2# Design region sizedr_size_x = (nx_phi -1) * ls_grid_sizedr_size_y = (ny_phi -1) * ls_grid_sizedr_center_x =-size_x /2+ w_length + dr_size_x /2# xy coordinates of the parameter and level set grids.x_rho = np.linspace(dr_center_x - dr_size_x /2, dr_center_x + dr_size_x /2, nx_rho)x_phi = np.linspace(dr_center_x - dr_size_x /2, dr_center_x + dr_size_x /2, nx_phi)y_rho = np.linspace(-dr_size_y /2, dr_size_y /2, ny_rho)y_phi = np.linspace(-dr_size_y /2, dr_size_y /2, ny_phi)
Level Set Functions
We are using autograd to implement a parameterized level set function so the gradients can be back-propagated from the permittivity distribution defined by the zero level set isocontour to the design variables (the control knots of the level set surface). The space between the control knots and the Gaussian function width obtains some control over the minimum feature size. Other types of radial basis functions can also be used in replacement of the Gaussian one employed here, such as multiquadric splines or b-splines.
To map the permittivities to the zero-level set contour and obtain continuous derivatives, we use a hyperbolic tangent function as an approximation to a Heaviside function. Other smooth functions, such as sigmoid and arctangent, can also be employed. As discussed here, the difference on computed interface using different functions will decrease when reducing the mesh size.
def mirror_param(design_param): param = anp.array(design_param).reshape((nx_rho, int(ny_rho /2)))try: param_minus = param._value.copy()except: param_minus = param.copy()return anp.concatenate((anp.fliplr(param_minus), param), axis=1).flatten()def get_eps(design_param, sharpness=10.0, plot_levelset=False) -> np.ndarray:"""Returns the permittivities defined by the zero level set isocontour""" phi_model = LevelSetInterp(x0=x_rho, y0=y_rho, z0=design_param, sigma=rho_size) phi = phi_model.get_ls(x1=x_phi, y1=y_phi)# Calculates the permittivities from the level set surface eps_phi =0.5* (anp.tanh(sharpness * phi) +1) eps = eps_min + (eps_max - eps_min) * eps_phi eps = anp.maximum(eps, eps_min) eps = anp.minimum(eps, eps_max)# Reshapes the design parameters into a 2D matrix. eps = anp.reshape(eps, (nx_phi, ny_phi))# Plots the level set surface.if plot_levelset: rho = np.reshape(design_param, (nx_rho, ny_rho)) phi = np.reshape(phi, (nx_phi, ny_phi)) plot_level_set(x0=x_rho, y0=y_rho, rho=rho, x1=x_phi, y1=y_phi, phi=phi)return eps
In the next function, the permittivity values are used to build a CustomMedium within the design region.
def update_design(eps, unfold=False) -> List[td.Structure]:# Reflects the structure about the x-axis. eps_val = anp.array(eps).reshape((nx_phi, ny_phi, 1)) coords_x = [(dr_center_x - dr_size_x /2) + ix * ls_grid_size for ix inrange(nx_phi)]ifnot unfold:# Creation of a CustomMedium using the values of the design parameters. coords_yp = [0+ iy * ls_grid_size for iy inrange(int(ny_phi /2))] coords =dict(x=coords_x, y=coords_yp, z=[0]) eps_ag = td.SpatialDataArray(eps_val, coords=coords) eps_medium = td.CustomMedium(permittivity=eps_ag) box = td.Box( center=(dr_center_x, dr_size_y /4, 0), size=(dr_size_x, dr_size_y /2, w_thick), ) structure = [td.Structure(geometry=box, medium=eps_medium)]else:# Creation of a CustomMedium using the values of the design parameters. coords_y = [-dr_size_y /2+ iy * ls_grid_size for iy inrange(ny_phi)] coords =dict(x=coords_x, y=coords_y, z=[0]) eps_ag = td.SpatialDataArray(eps_val, coords=coords) eps_medium = td.CustomMedium(permittivity=eps_ag) box = td.Box(center=(dr_center_x, 0, 0), size=(dr_size_x, dr_size_y, w_thick)) structure = [td.Structure(geometry=box, medium=eps_medium)]return structure
Initial Structure
We built an initial y-brach structure containing some holes and different gap sizes to demonstrate how the design evolves under fabrication constraints. We define this structure using a PolySlab object and then translate it into a permittivity grid of the same size as the one used to define the level set function. The holes are introduced in the polygon using the ClipOperation object.
Then an objective function which compares the initial structure and the permittivity distribution generated by the level set zero contour is defined.
# Figure of Merit (FOM) calculation.def fom_eps(eps_ref: anp.ndarray, eps: anp.ndarray) ->float:"""Calculate the L2 norm between eps_ref and eps."""return anp.mean(anp.abs(eps_ref - eps) **2)# Objective function to be passed to the optimization algorithm.def obj_eps(design_param, eps_ref) ->float: param = mirror_param(design_param) eps = get_eps(param)return fom_eps(eps_ref, eps)# Function to calculate the objective function value and its# gradient with respect to the design parameters.obj_grad_eps = value_and_grad(obj_eps)
So, the initial control knots are obtained after fitting the initial structure using the level set function. This is accomplished by minimizing the L2 norm between the reference and the level set generated permittivities with Tidy3D’s built-in Adam optimizer.
# Initialize adam optimizer with starting parameters.start_par = np.zeros(npar)def fit_objective(design_param):return obj_eps(design_param, init_eps)def report_step(params_eps, gradient, state, step_index, objective_val):print(f"Step = {step_index +1}")print(f"\tobj_eps = {objective_val:.4e}")print(f"\tgrad_norm = {np.linalg.norm(gradient):.4e}")params_eps, opt_state, history = optimize( fit_objective, params0=np.copy(start_par), optimizer=adam(learning_rate=learning_rate *10), num_steps=50, callback=report_step,)# Gets the final parameters and the objective values history.init_rho = np.copy(params_eps)obj_eps =list(history["objective_fn_val"])obj_vals_eps = np.array(obj_eps)
Here, one can see the initial parameters, which are the control knots defining the level set surface. The geometry of the structure will change as the zero isocontour evolves. The width of the Gaussian radial basis functions and the spacing of the control knots impact the accuracy and the smoothness of the initial zero-level set contour.
Next, we will write a function to return the Simulation object. Note that we are using a MeshOverrideStructure to obtain a uniform mesh over the design region.
The elements that do not change along the optimization are defined first.
# Input waveguide.wg_input = td.Structure( geometry=td.Box.from_bounds( rmin=(-eff_inf, -w_width /2, -w_thick /2), rmax=(-size_x /2+ w_length + grid_size, w_width /2, w_thick /2), ), medium=mat_si,)# Output bends.x_start = (-size_x /2+ w_length + dr_size_x - grid_size) # x-coordinate of the starting point of the waveguide bends.x = np.linspace(x_start, x_start + bend_length, 100) # x-coordinates of the top edge vertices.y = ( (x - x_start) * bend_offset / bend_length- bend_offset * np.sin(2* np.pi * (x - x_start) / bend_length) / (np.pi *2)+ (w_gap + w_width) /2) # y coordinates of the top edge vertices# adding the last point to include the straight waveguide at the outputx = np.append(x, eff_inf)y = np.append(y, y[-1])# add path to the cellcell = gdstk.Cell("bend")cell.add(gdstk.FlexPath(x +1j* y, w_width, layer=1, datatype=0)) # Top waveguide bend.cell.add(gdstk.FlexPath(x -1j* y, w_width, layer=1, datatype=0)) # Bottom waveguide bend.# Define top waveguide bend structure.wg_bend_top = td.Structure( geometry=td.PolySlab.from_gds( cell, gds_layer=1, axis=2, slab_bounds=(-w_thick /2, w_thick /2), )[1], medium=mat_si,)# Define bottom waveguide bend structure.wg_bend_bot = td.Structure( geometry=td.PolySlab.from_gds( cell, gds_layer=1, axis=2, slab_bounds=(-w_thick /2, w_thick /2), )[0], medium=mat_si,)
Monitors used to get simulation data.
# Input mode source.mode_spec = td.ModeSpec(num_modes=1, target_neff=nSi)source = td.ModeSource( center=(-size_x /2+0.15* wl, 0, 0), size=(0, mon_w, mon_h), source_time=td.GaussianPulse(freq0=freq, fwidth=freqw), direction="+", mode_spec=mode_spec, mode_index=0,)# Monitor where we will compute the objective function from.fom_monitor_1 = td.ModeMonitor( center=[size_x /2-0.25* wl, (w_gap + w_width) /2+ bend_offset, 0], size=[0, mon_w, mon_h], freqs=[freq], mode_spec=mode_spec, name=fom_name_1,)# Monitors used only to visualize the initial and final y-branch results.# Field monitors to visualize the final fields.field_xy = td.FieldMonitor( size=(td.inf, td.inf, 0), freqs=[freq], name="field_xy",)# Monitor where we will compute the objective function from.fom_final_1 = td.ModeMonitor( center=[size_x /2-0.25* wl, (w_gap + w_width) /2+ bend_offset, 0], size=[0, mon_w, mon_h], freqs=freqs, mode_spec=mode_spec, name="out_1",)
And then the Simulation is built.
def make_adjoint_sim(design_param, unfold=True) -> td.Simulation:# Builds the design region from the design parameters. eps = get_eps(design_param) design_structure = update_design(eps, unfold=unfold)# Creates a uniform mesh for the design region. adjoint_dr_mesh = td.MeshOverrideStructure( geometry=td.Box(center=(dr_center_x, 0, 0), size=(dr_size_x, dr_size_y, w_thick)), dl=[grid_size, grid_size, grid_size], enforce=True, )return td.Simulation( size=[size_x, size_y, size_z], center=[0, 0, 0], grid_spec=td.GridSpec.auto( wavelength=wl_max, min_steps_per_wvl=15, override_structures=[adjoint_dr_mesh], ), symmetry=(0, -1, 1), structures=[wg_input, wg_bend_top, wg_bend_bot] + design_structure, sources=[source], monitors=[fom_monitor_1], run_time=run_time, subpixel=True, )
Let’s visualize the simulation setup and verify if all the elements are in their correct places. Differently from the density-based methods, we will start from a fully binarized structure.
20:47:31 UTC Estimated FlexCredit cost: 0.025. This assumes the FDTD solver runs
for the full simulation time; if early shutoff is reached, the
billed cost can be lower. Use 'web.real_cost(task_id)' to get the
billed FlexCredit cost after a simulation run.
20:47:32 UTC status = success
20:47:33 UTC Loading results from simulation_data.hdf5
We will use the insertion loss (IL) to compare the device response before and after the optimization. Since we will use symmetry about the y-axis, the insertion loss is calculated as \(IL = -10 log(2P_{1}/P_{in})\), where \(P_{1}\) is the power coupled into the upper s-bend and \(P_{in}\) is the unit input power. The insertion loss of the non-optimized y-branch is above 3 dB at 1.55 \(\mu m\). From the field distribution image, we can realize that it happens because much of the input power is reflected.
Fabrication constraints are introduced in the optimization as penalty terms to control the minimum gap (\(f_{g}\)) and radius of curvature (\(f_{c}\)) in the final design. Below, we use autograd to define the penalty terms following the formulation presented in D. Vercruysse, N. V. Sapra, L. Su, R. Trivedi, and J. Vučković, "Analytical level set fabrication constraints for inverse design," Scientific Reports 9, 8999 (2019).DOI: 10.1038/s41598-019-45026-0. The gap penalty function controls the minimum feature size by limiting the second derivative based on the value of the function at that point. The curvature constraint is only relevant at the device boundary, where \(\phi = 0\), so we apply the smoothed Heaviside function to the level set surface before calculating the derivatives.
# Auxiliary function to calculate first and second order partial derivatives.def ls_derivatives(phi, d_size): SC =1e-12 phi_1 = anp.array(anp.gradient(phi)) / d_size phi_x = phi_1[0] + SC phi_y = phi_1[1] + SC phi_2x = anp.array(anp.gradient(phi_x)) / d_size phi_2y = anp.array(anp.gradient(phi_y)) / d_size phi_xx = phi_2x[0] phi_xy = phi_2x[1] phi_yy = phi_2y[1]return phi_x, phi_y, phi_xx, phi_xy, phi_yy# Minimum gap size fabrication constraint integrand calculation.# The "beta" parameter relax the constraint near the zero plane.def fab_penalty_ls_gap(params, beta=1, min_feature_size=min_feature_size, grid_size=ls_grid_size):# Get the level set surface. phi_model = LevelSetInterp(x0=x_rho, y0=y_rho, z0=params, sigma=rho_size) phi = phi_model.get_ls(x1=x_phi, y1=y_phi) phi = anp.reshape(phi, (nx_phi, ny_phi))# Calculates their derivatives. phi_x, phi_y, phi_xx, phi_xy, phi_yy = ls_derivatives(phi, grid_size)# Calculates the gap penalty over the level set grid. pi_d = np.pi / (1.3* min_feature_size) phi_v = anp.maximum(anp.power(phi_x**2+ phi_y**2, 0.5), anp.power(1e-32, 1/4)) phi_vv = (phi_x**2* phi_xx +2* phi_x * phi_y * phi_xy + phi_y**2* phi_yy) / phi_v**2return ( anp.maximum((anp.abs(phi_vv) / (pi_d * anp.abs(phi) + beta * phi_v) - pi_d), 0)* grid_size**2 )# Minimum radius of curvature fabrication constraint integrand calculation.# The "alpha" parameter controls its relative weight to the gap penalty.# The "sharpness" parameter controls the smoothness of the surface near the zero-contour.def fab_penalty_ls_curve( params, alpha=1, sharpness=1, min_feature_size=min_feature_size, grid_size=ls_grid_size,):# Get the permittivity surface and calculates their derivatives. eps = get_eps(params, sharpness=sharpness) eps_x, eps_y, eps_xx, eps_xy, eps_yy = ls_derivatives(eps, grid_size)# Calculates the curvature penalty over the permittivity grid. pi_d = np.pi / (1.1* min_feature_size) eps_v = anp.maximum(anp.sqrt(eps_x**2+ eps_y**2), anp.power(1e-32, 1/6)) k = (eps_x**2* eps_yy -2* eps_x * eps_y * eps_xy + eps_y**2* eps_xx) / eps_v**3 curve_const = anp.abs(k * anp.arctan(eps_v / eps)) - pi_d curve_const = anp.nan_to_num(curve_const)return alpha * anp.maximum(curve_const, 0) * grid_size**2# Gap and curvature fabrication constraints calculation.# Penalty values are normalized by "norm_gap" and "norm_curve".def fab_penalty_ls( params, beta=gap_par, alpha=curve_par, sharpness=4, min_feature_size=min_feature_size, grid_size=ls_grid_size, norm_gap=1, norm_curve=1,):# Get the gap penalty fabrication constraint value. gap_penalty_int = fab_penalty_ls_gap( params=params, beta=beta, min_feature_size=min_feature_size, grid_size=grid_size ) gap_penalty_int = anp.nan_to_num(gap_penalty_int) gap_penalty = anp.sum(gap_penalty_int) / norm_gap# Get the curvature penalty fabrication constraint value. curve_penalty_int = fab_penalty_ls_curve( params=params, alpha=alpha, sharpness=sharpness, min_feature_size=min_feature_size, grid_size=grid_size, ) curve_penalty_int = anp.nan_to_num(curve_penalty_int) curve_penalty = anp.sum(curve_penalty_int) / norm_curvereturn gap_penalty, curve_penalty
Now, we will calculate the initial penalty function values and observe the regions of the initial design that violate the constraints. The gap and curvature penalty functions are normalized by their initial values along the optimization to better balance the weights of device response and fabrication penalty within the objective function.
The figure-of-merit used in the y-branch optimization is the power (\(P_{1, 2}\)) coupled into the fundamental transverse electric mode of the output waveguides. We will set mirror symmetry about the y-axis in the optimization, so we must include only \(P_{1}\) in the figure-of-merit. As we are using a minimization strategy, the coupled power and fabrication constraints are arranged within the objective function as \(|0.5 - P_{1}| + w_{f} \times (f_{g} + f_{c})\), where \(w_{f}\) is the fabrication constraint weight, whereas \(f_{g}\) and \(f_{c}\) are the gap and curvature penalty values.
# Figure of Merit (FOM) calculation.def fom(sim_data: td.SimulationData) ->float:"""Return the power at the mode index of interest.""" output_amps1 = sim_data[fom_name_1].amps amp1 = output_amps1.sel(direction="+", f=freq, mode_index=0) eta1 = anp.sum(anp.nan_to_num(anp.abs(amp1.values)) **2)return anp.abs(0.5- eta1), eta1# Objective function to be passed to the optimization algorithm.def obj( design_param, fab_const: float=0.0, norm_gap=1.0, norm_curve=1.0, verbose: bool=False,) ->float: param = mirror_param(design_param) sim = make_adjoint_sim(param) sim_data = web.run(sim, task_name="inv_des_ybranch", verbose=verbose) fom_val, eta1 = fom(sim_data) fab_gap, fab_curve = fab_penalty_ls(param, norm_gap=norm_gap, norm_curve=norm_curve) J = fom_val + fab_const * (fab_gap + fab_curve) aux_data = anp.array([sim_data, getval(eta1), getval(fab_gap), getval(fab_curve)])return (J, aux_data)# Function to calculate the objective function value and its# gradient with respect to the design parameters.obj_grad = value_and_grad(obj, has_aux=True)
Optimizer initialization
# where to store historyhistory_fname ="./misc/y_branch_fab.pkl"def save_history(history_dict: dict) ->None:"""Convenience function to save the history to file."""withopen(history_fname, "wb") asfile: pickle.dump(history_dict, file)def load_history() ->dict:"""Convenience method to load the history from file."""withopen(history_fname, "rb") asfile: history_dict = pickle.load(file)return history_dict
Before starting, we will look for data from a previous optimization.
Below, we can see how the device response and fabrication penalty have evolved throughout the optimization. The coupling into the output waveguide improves quickly in the beginning at the expense of higher penalty values. Then, the penalty values decrease linearly after the device response achieves a near-optimal condition. This trend results from the small weight factor we have chosen for the fabrication penalty terms.
We can also see a significant reduction in violations to the minimum feature size after the optimization, which results in a smoother structure. The optimized device has not matched the minimum feature size exactly. The minimum radius of curvature and gap size are about 30% higher and 20% lower than the reference feature size, respectively. This deviation is expected, as reported in the previous paper. In this regard, running the simulation longer, adjusting the penalty weight or compensating for the reference feature size could improve the results.
21:16:07 UTC Estimated FlexCredit cost: 0.025. This assumes the FDTD solver runs
for the full simulation time; if early shutoff is reached, the
billed cost can be lower. Use 'web.real_cost(task_id)' to get the
billed FlexCredit cost after a simulation run.
21:16:08 UTC status = queued
To cancel the simulation, use 'web.abort(task_id)' or
'web.delete(task_id)' or abort/delete the task in the web UI.
Terminating the Python script will not stop the job running on the
cloud.
21:16:16 UTC status = preprocess
21:16:20 UTC starting up solver
21:16:21 UTC running solver
21:16:26 UTC early shutoff detected at 57%, exiting.
The Simulation object has the .to_gds_file convenience function to export the final design to a GDS file. In addition to a file name, it is necessary to set a cross-sectional plane (z = 0 in this case) on which to evaluate the geometry, a frequency to evaluate the permittivity, and a permittivity_threshold to define the shape boundaries in custom mediums. See the GDS export notebook for a detailed example on using .to_gds_file and other GDS related functions.
We use necessary cookies to run this website. With your permission, we
also use analytics cookies to understand site usage and marketing
cookies for advertising, retargeting, and HubSpot tracking.
Learn more about our cookie policy.
Privacy choices
Choose which optional cookies Flexcompute may use. Necessary cookies
are always on because they support core website behavior, security, and
saving your consent record.
Your browser is sending a Global Privacy Control signal, so marketing
cookies are disabled.
Subscribe
Thanks for subscribing
Publish Your Notebook
Thank you for publishingA confirmation email has been sent to your inbox. Your notebook will be available within the next 48 hours.
Community Library
TERMS & CONDITIONS
EFFECTIVE DATE: January 1, 2025
Terms and Conditions for User-Submitted Content
By submitting content to Flexcompute, you agree to the following terms:
1. Ownership and Copyright
All content, including but not limited to text, data, images, and other materials submitted by users,
remains the sole property of the original creator. Flexcompute does not claim ownership of the submitted
content or any intellectual property rights associated with it.
Users affirm that they own the copyright or have obtained all necessary permissions for the submitted
content and are fully responsible for ensuring that their submissions do not infringe on any third-party
rights.
2. Responsibility for Content
Users are solely responsible for the content they submit. Flexcompute does not endorse, guarantee,
or verify the accuracy, legality, or appropriateness of any submitted content. Users are responsible
for ensuring that their content complies with all applicable laws and regulations.
Users declare that they have obtained authorization from all co-authors and collaborators associated
with the submitted content, granting them the right to submit and sign on behalf of all contributors.
Users agree not to submit content that is illegal, defamatory, obscene, or violates the rights
of others, including privacy and intellectual property rights.
3. Modification and Presentation
Flexcompute reserves the right to review, edit, or modify submitted content to improve clarity,
formatting, and presentation while maintaining the original intent and message.
These modifications are made to enhance the overall quality and readability of the published material.
Users acknowledge that Flexcompute may format or present the content in a way
that aligns with our editorial and visual standards.
4. Liability Disclaimer
Flexcompute shall not be held liable for any disputes arising from user-submitted content, including
but not limited to copyright claims, inaccuracies, or damages resulting from the publication of user content.
Users agree to indemnify Flexcompute against any claims or legal actions resulting from their submitted content.
5. Content Use Rights
By submitting content, users grant Flexcompute a non-exclusive, royalty-free, worldwide license to publish, distribute,
and promote the content for the purposes of showcasing user contributions and marketing our services.
This license does not transfer ownership of the content or any copyright to Flexcompute.
Users retain the right to withdraw their submissions at any time.
Upon request, Flexcompute will remove the content from its platform.
6. Privacy and Confidentiality
Users acknowledge that submitted content may be publicly accessible and therefore waive any
rights to confidentiality or privacy regarding the published material.
Flexcompute will not share personal information of users without
their consent, in accordance with our privacy policy.
Community Library
How the process works:
You submit your notebook.
You will get a permanent URL to promote your work.
Cite the URL as a reference for others in your upcoming papers to enhance the impact.
When someone clicks your URL, your notebook will be displayed.
Enter your email address below to receive the presentation slides. In the future, we’ll share you very few emails when we have new tutorial release, development updates, valuable toolkits and technical guidance . You can unsubscribe at any time by clicking the link at the bottom of every email. We’ll never share your information.