Using FiberFactory

The FiberFactory is a convenient class that allows us to build a Fiber, or a family of fibers. A FiberFactory defines a list of layers. Each fiber built from a FiberFactory share the same structure of layers. Layers are defined from the center of the fiber (layer 0) to the exterior. Each layer have a specific geometry and material.

Geometry and material

The geometry describes the relation between the refractive index of the layer, and the radial position. Actually, the only choice is StepIndex, which means the refractive index is uniform across the fiber layer. In the future, it could be possible to define other geometries, to define graded index fibers, for instance.

The material describes the relation between the refractive index of the layer, and the wavelength of the light. The simplest material is Fixed, which means that the refractive index is independent of the wavelength. Some materials, like Air or Silica only have dependency on the wavelength. Other materials, like SiO2GeO2 or SiO2F also depend on the concentration of a dopant.

Defining fiber layers

The first step is to create a new empty FiberFactory object:

from fibermodes import FiberFactory
factory = FiberFactory()

If we want, we can add some metadata to the fiber factory. This is especially useful when we save a fiber design in a file, and we want to remember later what was that fiber. Metadata are accessible as properties:

factory.name = "Name of your fiber"
factory.author = "Your Name"
factory.description = "Description of your fiber"

You can also get the creation date and time, and when the fiber was last saved to a file:

crdate = factory.crdate
tstamp = factory.tstamp

The addLayer() method is used to create layers. With no arguments, it creates a StepIndex Fixed layer, at outer position, with refractive index of 1.444:

factory.addLayer()

Parameters can be given to specify position, name, geometry, and material. For example, to create a step-index layer named “core”, of 4 µm radius, with Silica doped with 10% of Germania:

factory.addLayer(pos=0, name="core", geometry="StepIndex", radius=4e-6,
                 material="SiO2GeO2", x=0.1)

It is also possible to specify index and wavelength, and the material will compute the right concentration:

factory.addLayer(pos=1, name="trench", radius=6e-6, material="SiO2F",
                 index=1.441, wl=1550e-9)

Getting information form layers in FiberFactory

Is is possible to get informations about the layers in an already created FiberFatory, using the layers() property. For example:

print(factory.layers[0].name)
print(factory.layers[1].radius)
factory.layers[2].name = "cladding"

Creating families of fibers

All fiber parameters can be specified either as a scalar value, a list, a range, or as code to be executed. We already covered the assignation of scalar values in previous examples.

Specifying parameters as list

Suppose we want to simulate fibers with varying core radius. We can build the FiberFactory like this:

factory = FiberFactory()
factory.addLayer(name="core", radius=[1e-6, 2e-6, 3e-6, 4e-6, 5e-6],
                 index=1.474)
factory.addLayer()

We could now compute the effective index of the HE(1,1) mode, for each fiber:

for i, fiber in enumerate(factory):
    print(factory.layers[0].radius[i], fiber.neff(Mode("HE", 1, 1), 1550e-9))

Suppose now we want to plot effective index as function of core index:

from matplotlib import pyplot
import numpy

n = numpy.linspace(1.454, 1.494)
factory = FiberFactory()
factory.addLayer(name="core", radius=4e-6, index=n)
factory.addLayer(name="cladding")

neff = numpy.zeros(n.shape)
for i, fiber in enumerate(factory):
    neff[i] = fiber.neff(Mode("HE", 1, 1), 1550e-9)


pyplot.plot(n, neff)
pyplot.show()
../_images/ncoreneff.png

Generating the fibers

In the previous example, we generated the fibers by iterating over the factory object. For example, to generate all fibers in a list, we can do:

fibers = list(factory)

We can also access fibers by index. For example, to get the fifth fiber:

fiber = factory[4]

If multiple parameters are specified as list, the product of parameter combinations is generated. For example, if there are two core radii, and three core indexes, there will be six different fibers generated. The number of generated fibers can be queried using the len function.

Specifying parameters as range

Another way to specify parameters is to specify a range. The range itself is defined using a dict, with start, end, and num keys. If many parameters contain multiple values, fibers with all combination of parameters are built. For example:

factory = FiberFactory()
factory.addLayer(name="core", radius={'start': 2e-6, 'end': 5e-6, 'num': 10},
                 index=[1.454, 1.464, 1.474])
factory.addLayer(name="cladding")
print(len(factory))

would print 30, because there are 10 radius combined with 3 indexes.

When the FiberFactory build fibers, it removes unneeded layers. For example if two consecutive layer have the same refractive index, or if the layer radius is 0. Therefore, the resulting fiber could have a smaller number of layer that the number of layers in the FiberFactory itself.

Specifying parameters as code

The last way to specify parameter is to write Python code. This is the only way to make a parameter depend on another parameter. The function is defined like this:

def f(r, fp, mp):
    # You code goes here

It is expected to return a single value. The simplest possible code would be something like this:

factory = FiberFactory()
factory.addLayer(radius="return 4e-6", index="return 1.474")
factory.addLayer()

Off course, this example is useless, as you should directly pass the value.

The three parameters you receive are the layer parameters. r is a list that contain the radius of each layer, e.g. r[0] is the radius of the center layer, and so on. fp, for fiber parameters, is a list containing the list of geometry parameters for each layer. For StepIndex geometry, it is unused. mp, for material parameters, is a list containing the list of material parameters. Actually, it is only useful for doped material, like SiO2GeO2. For example, if the second layer is SiO2GeO2, its molar concentration is given by mp[1][0].

Code is executed from center layer to the exterior. Therefore, it is possible to refer a value from an computer inner layer, but not from a computer outer layer.

Suppose you want to define a family of ring-core fibers, where the ring is 2 µm thick. You could do it like this:

factory = FiberFactory()
factory.addLayer(radius={'start': 1e-6, 'end': 10e-6, 'num': 10})
factory.addLayer(radius="return r[0] + 2e-6", index=1.474)
factory.addLayer()

Now, if you want to define a family of fibers sharing different core radius, but the same V number (V=10):

code = """V = 10
ncl = 1.444
k0 = 2 * math.pi / 1550e-9
n = math.sqrt((V / (k0 * r[0]))**2 - ncl**2)
return n
"""
factory = FiberFactory()
factory.addLayer(radius={'start': 1e-6, 'end': 10e-6, 'num': 10},
                 index=code)
factory.addLayer(index=1.444)

Loading and saving factory objects

FiberFactory has an interface similar to json. You can load and save the factory to a json file, or to a json string, using load(), dump(), loads(), or dumps() methods:

with open('myfiber.json', 'w') as f:
    factory.dump(f)

with open('myfiber.json', 'r') as f:
    factory.load(f)

You can also give the file name to the FiberFactory constructor:

factory = FiberFactory('myfiber.json')

When you save the factory to a file, the timestamp is update automatically. Therefore, you always know when the object was last modified.