Commit 8c37dc8c authored by Alexei Ciobanu's avatar Alexei Ciobanu

Merge remote-tracking branch 'upstream/master'

parents 1c49963b a0e09ae5
This source diff could not be displayed because it is too large. You can view the blob instead.
=========================
``finesse.detectors.ccd``
=========================
================================
``finesse.detectors.camera.CCD``
================================
.. automodule:: finesse.detectors.ccd
.. currentmodule:: finesse.detectors.ccd
.. currentmodule:: finesse.detectors.camera
Overview
......@@ -25,7 +25,8 @@ Properties
:toctree: generated/
CCD.is_waist_scaled
CCD.x
CCD.y
Methods
......
===================================
``finesse.detectors.camera.Camera``
===================================
.. currentmodule:: finesse.detectors.camera
Overview
========
.. autoclass:: Camera
Properties
==========
.. autosummary::
:toctree: generated/
Camera.is_waist_scaled
Camera.is_monochromatic
Methods
=======
.. autosummary::
:toctree: generated/
Camera.__init__
Camera.get_output
\ No newline at end of file
==================================
``finesse.detectors.camera.Pixel``
==================================
.. currentmodule:: finesse.detectors.camera
Overview
========
.. autoclass:: Pixel
Methods
=======
.. autosummary::
:toctree: generated/
Pixel.__init__
Pixel.get_output
\ No newline at end of file
============================
``finesse.detectors.camera``
============================
.. automodule:: finesse.detectors.camera
.. currentmodule:: finesse.detectors.camera
Classes
=======
.. autosummary::
:toctree: camera/
Camera
CCD
Pixel
\ No newline at end of file
==========================
``finesse.detectors.gouy``
==========================
.. automodule:: finesse.detectors.gouy
.. currentmodule:: finesse.detectors.gouy
\ No newline at end of file
......@@ -11,7 +11,6 @@ dependencies:
- cython>=0.29
- networkx
- h5py
- pygraphviz
- pylint
- pip
- pip:
......
......@@ -158,3 +158,4 @@ def cli(ctx, file, start, stop, steps, legacy, ignored_blocks, plot, save_figure
return
print(out)
out.plot()
......@@ -9,7 +9,7 @@ a brief description of the contents of each.
{contents}
"""
from finesse.analysis.axes import X0Axis as noxaxis, X1Axis as xaxis, X2Axis as x2axis
from finesse.analysis.axes import X0Axis as noxaxis, X1Axis as xaxis, X2Axis as x2axis, X3Axis as x3axis
if __doc__:
from finesse.utilities.misc import _collect_submodules
......
......@@ -24,7 +24,8 @@ class BaseXAxis(BaseAnalysis, metaclass=abc.ABCMeta):
Parameters
----------
*args
pair of parameter and array to scan it over
triples of parameter, array to scan it over, and offset to
array values
**kwargs
Passed to `Model.build`
......@@ -36,12 +37,13 @@ class BaseXAxis(BaseAnalysis, metaclass=abc.ABCMeta):
if len(args) == 0 and self.model is not None:
models = [self.model]
elif len(args) % 2 != 0:
raise Exception("Arguments must be pairs of parameter followed by an array of values "
"to scan over")
elif len(args) % 3 != 0:
raise Exception("Arguments must be triples of parameter, array of "
"values to scan over, and offset to array values.")
params = args[::2]
axes = tuple(np.atleast_1d(_) for _ in args[1::2])
params = args[::3]
axes = tuple(np.atleast_1d(_) for _ in args[1::3])
offsets = args[2::3]
LOGGER.info("Scanning parameters %s", list(params))
......@@ -54,8 +56,8 @@ class BaseXAxis(BaseAnalysis, metaclass=abc.ABCMeta):
if models[0].is_built:
for p in params:
if not p.is_tunable:
raise ParameterLocked(f"{repr(p)} must set as tunable before buulding the "
"simulation")
raise ParameterLocked(f"{repr(p)} must set as tunable "
"before building the simulation")
else:
for p in params:
p.is_tunable = True
......@@ -97,8 +99,8 @@ class BaseXAxis(BaseAnalysis, metaclass=abc.ABCMeta):
shapes = tuple(np.size(_) for _ in axes)
for idx in np.ndindex(shapes):
for p, ax, i in zip(params, axes, idx):
p.value = ax[i]
for p, ax, i, offset in zip(params, axes, idx, offsets):
p.value = ax[i] + offset
carrier.run()
......@@ -139,7 +141,7 @@ class X1Axis(BaseXAxis):
This should provide an equivalent to the xaxis command in Finesse v2.
"""
def __init__(self, param, start, stop, steps, mode='lin', **kwargs):
def __init__(self, param, start, stop, steps, mode='lin', offset=0, **kwargs):
"""
Initialises an single xaxis simulation to later run.
......@@ -153,6 +155,9 @@ class X1Axis(BaseXAxis):
Number of steps between start and end
mode : str
'lin' or 'log' for linear or logarithmic step sizes
offset : float, optional
Offset to scanned values. For a given xaxis point,
`param` will be set to `x[i] + offset`.
**kwargs
Passed to `Model.Build` when runs
"""
......@@ -163,6 +168,7 @@ class X1Axis(BaseXAxis):
self.stop = stop
self.steps = steps
self.mode = mode
self.offset = offset
self.kwargs = kwargs
......@@ -176,6 +182,7 @@ class X1Axis(BaseXAxis):
"""
return super()._run(self.param,
self.sweep_vector(self.start, self.stop, self.steps, self.mode),
self.offset,
**self.kwargs)
......@@ -186,7 +193,8 @@ class X2Axis(BaseXAxis):
This should provide an equivalent to the x2axis command in Finesse v2.
"""
def __init__(self, param1, start1, stop1, steps1, param2, start2, stop2, steps2, mode1='lin', mode2='lin', **kwargs):
def __init__(self, param1, start1, stop1, steps1, param2, start2, stop2,
steps2, mode1='lin', mode2='lin', offset1=0, offset2=0, **kwargs):
"""
Initialises a 2D parameter sweep simulation to later run.
Param2 is in the inner loop and param1 in the outer loop.
......@@ -201,6 +209,9 @@ class X2Axis(BaseXAxis):
Number of steps between start and end
mode1, mode2 : str
'lin' or 'log' for linear or logarithmic step sizes for axis 1 and 2
offset1, offset2 : float, optional
Offsets to scanned values. For a given xaxis point,
`param` will be set to `x[i] + offset`.
**kwargs
Passed to `Model.Build` when run
"""
......@@ -212,11 +223,13 @@ class X2Axis(BaseXAxis):
self.stop1 = stop1
self.steps1 = steps1
self.mode1 = mode1
self.offset1 = offset1
self.param2 = param2
self.start2 = start2
self.stop2 = stop2
self.steps2 = steps2
self.mode2 = mode2
self.offset2 = offset2
self.kwargs = kwargs
def _run(self):
......@@ -227,6 +240,73 @@ class X2Axis(BaseXAxis):
:class:`.ArraySolution`
The results of the axis sweep.
"""
return super()._run(self.param1, self.sweep_vector(self.start1, self.stop1, self.steps1, self.mode1),
self.param2, self.sweep_vector(self.start2, self.stop2, self.steps2, self.mode2),
return super()._run(self.param1, self.sweep_vector(self.start1, self.stop1, self.steps1, self.mode1), self.offset1,
self.param2, self.sweep_vector(self.start2, self.stop2, self.steps2, self.mode2), self.offset2,
**self.kwargs)
class X3Axis(BaseXAxis):
"""Runs a model to scan a parameter between two points for a number of steps.
The model that is run is retrieved from the parameter reference.
This should provide an equivalent to the x3axis command in Finesse v2.
"""
def __init__(self, param1, start1, stop1, steps1, param2, start2, stop2,
steps2, param3, start3, stop3, steps3, mode1='lin', mode2='lin',
mode3='lin', offset1=0, offset2=0, offset3=0, **kwargs):
"""
Initialises a 3D parameter sweep simulation to later run.
Param3 is in the inner loop and param1 in the outer loop.
Parameters
----------
param1, param2, param3 : :class:`.ModelParameter`
Parameter of component to scan
start1, stop1, start2, stop2, start3, stop3 : float
Start and end values of the scan
steps1, steps2, steps3 : int
Number of steps between start and end
mode1, mode2, mode3 : str
'lin' or 'log' for linear or logarithmic step sizes for each axis
offset1, offset2, offset3 : float, optional
Offsets to scanned values. For a given xaxis point,
`param` will be set to `x[i] + offset`.
**kwargs
Passed to `Model.Build` when run
"""
assert(param1._model is param2._model)
super().__init__(param1._model)
self.param1 = param1
self.start1 = start1
self.stop1 = stop1
self.steps1 = steps1
self.mode1 = mode1
self.offset1 = offset1
self.param2 = param2
self.start2 = start2
self.stop2 = stop2
self.steps2 = steps2
self.mode2 = mode2
self.offset2 = offset2
self.param3 = param3
self.start3 = start3
self.stop3 = stop3
self.steps3 = steps3
self.mode3 = mode3
self.offset3 = offset3
self.kwargs = kwargs
def _run(self):
"""Run simulation.
Returns
-------
:class:`.ArraySolution`
The results of the axis sweep.
"""
return super()._run(self.param1, self.sweep_vector(self.start1, self.stop1, self.steps1, self.mode1), self.offset1,
self.param2, self.sweep_vector(self.start2, self.stop2, self.steps2, self.mode2), self.offset2,
self.param3, self.sweep_vector(self.start3, self.stop3, self.steps3, self.mode3), self.offset3,
**self.kwargs)
......@@ -56,7 +56,7 @@ class BaseAnalysis(metaclass=abc.ABCMeta):
The simulation results.
"""
self._pre_run(**kwargs)
self._run(*args, **kwargs)
self._run()
self._post_run(**kwargs)
if validate:
self._eval_constraints()
......
This diff is collapsed.
......@@ -5,6 +5,7 @@ around components inside a cavity.
from copy import copy
import cmath
import math
import numpy as np
......@@ -419,6 +420,11 @@ class Cavity(ModelElement):
return not (self.__stability_x < 0.0 or self.__stability_x > 1.0
or self.__stability_y < 0.0 or self.__stability_y > 1.0)
@property
def is_critical(self):
return (self.__stability_x == 0.0 or self.__stability_y == 0.0 or
math.isclose(self.__stability_x, 1.0) or math.isclose(self.__stability_y, 1.0))
def _update_round_trip_length(self):
rt_L = 0.0
for from_node, to_comp in self.__path.data:
......
......@@ -169,7 +169,7 @@ class Connector(ModelElement):
self._abcd_matrices = {}
# flag for recomputing ABCD (rather than grabbing from _abcd_matrices dict)
self._recompute_abcd = True
self._recompute_abcd = {}
def __repr__(self):
return "<'{}' @ {} ({})>".format(self.name, hex(id(self)), self.__class__.__name__)
......@@ -442,7 +442,7 @@ class Connector(ModelElement):
def _store_ABCD(self, M, from_node, to_node, direction):
self._abcd_matrices[(from_node, to_node, direction)] = M
self._recompute_abcd = False
self._recompute_abcd[(from_node, to_node, direction)] = False
def log_knm(self, loggable, couplings=None):
"""Select the knm matrices to log and, optionally, which mode
......@@ -692,7 +692,8 @@ class Surface(ABC, Connector):
def _check_Rc(self, value):
if self.has_model:
self._model._retrace_required = True
self._recompute_abcd = True
self._recompute_abcd = self._recompute_abcd.fromkeys(self._recompute_abcd, True)
return value
......
......@@ -9,7 +9,7 @@ from finesse.components.node import NodeDirection, NodeType
from finesse.element import model_parameter, Rebuild
@model_parameter('S', 0.0, Rebuild.PlaneWave, "_check_S")
@model_parameter('S', 0.0, Rebuild.PlaneWave)
class Isolator(Connector):
"""
A class representing an isolator optical component with a suppression
......@@ -45,18 +45,14 @@ class Isolator(Connector):
self._register_node_coupling(self.p2.i, self.p1.o,
interaction_type= InteractionType.TRANSMISSION)
def _check_S(self, value):
self.__suppression_factor = 10**(-float(value) / 20)
def _on_build(self, sim):
self.__I = np.eye(sim.nhoms, dtype=np.complex128)
def _fill_matrix(self, sim):
values, is_changing = self._eval_parameters()
suppression = 10**(-values["S"] / 20)
for freq in sim.frequencies:
with sim.component_edge_fill(self, 'p1.i->p2.o', freq, freq) as mat:
mat[:] = self.__I[:]
with sim.component_edge_fill(self, 'p2.i->p1.o', freq, freq) as mat:
np.multiply(
self.__suppression_factor,
self.__I,
out=mat[:])
np.multiply(suppression, self.__I, out=mat[:])
......@@ -88,8 +88,8 @@ class Laser(Connector, FrequencyGenerator):
raise ValueError("n + m larger than maximum order for TEM.")
self.__power_coeffs[(n, m)] = zrotate(complex(np.sqrt(factor), 0), np.radians(phase))
def _update_tem_gouy_phases(self, sim):
if not self._model._trace_changed:
def _update_tem_gouy_phases(self, sim, force=False):
if not force and not self._model._trace_changed:
return
qx, qy, _ = sim.model.last_trace[self.p1.o]
......
......@@ -76,7 +76,8 @@ class Lens(Connector):
def _check_f(self, value):
if np.isclose(value, 0.0):
raise ValueError("Focal length of lens must be non-zero.")
self._recompute_abcd = True
self._recompute_abcd = self._recompute_abcd.fromkeys(self._recompute_abcd, True)
if self.has_model:
self._model._retrace_required = True
......@@ -134,11 +135,12 @@ class Lens(Connector):
or if `from_node` and `to_node` are both inputs or both outputs.
"""
self._ABCD_prechecks(from_node, to_node)
values, _ = self._eval_parameters()
if symbolic:
raise NotImplementedError()
else:
f = self.f.value
f = values['f']
underlying = [
[1.0, 0.0],
......@@ -199,11 +201,12 @@ class Lens(Connector):
# zero the phase of TEM00 -> TEM00 coefficient
if sim.model.phase_config.ZERO_K00: zero_tem00_phase(Knm)
def _compute_knm_matrices(self, sim):
if not sim.is_modal or not self._model._trace_changed:
LOGGER.info("No knm dependent parameters have changed, skipping recomputation "
f"of knm matrices for: {self.name}")
return
def _compute_knm_matrices(self, sim, force=False):
if not force:
if not sim.is_modal or not self._model._trace_changed:
LOGGER.info("No knm dependent parameters have changed, skipping recomputation "
f"of knm matrices for: {self.name}")
return
LOGGER.info(f"Computing knm matrices for: {self.name}")
......
......@@ -120,7 +120,7 @@ def beamsplitter_fill(beamsplitter, sim, dict values, double lambda0):
fval = <double>freq.f
phi_scaled = phi * (1 + fval / f0)
_tuning = cexp(-2.0j * phi_scaled * _cos_alpha)
_tuning = cexp(2.0j * phi_scaled * _cos_alpha)
_ctuning = conj(_tuning)
# Need to conjugate lower sideband in audio calculations
......
......@@ -267,9 +267,9 @@ class Mirror(Surface):
if symbolic:
raise NotImplementedError()
if not symbolic:
if not self._recompute_abcd:
stored_M = self._abcd_matrices.get(
(from_node, to_node, direction), None)
recompute = self._recompute_abcd.get((from_node, to_node, direction), True)
if not recompute:
stored_M = self._abcd_matrices.get((from_node, to_node, direction), None)
if stored_M is not None:
return stored_M
......@@ -279,11 +279,12 @@ class Mirror(Surface):
to_node = self.nodes[to_node]
self._ABCD_prechecks(from_node, to_node)
values, _ = self._eval_parameters()
if direction == 'x':
Rc = self.Rcx.value
Rc = values["Rcx"]
else:
Rc = self.Rcy.value
Rc = values["Rcy"]
if self.interaction_type(from_node, to_node) == InteractionType.REFLECTION:
fn_space = from_node.space
......@@ -598,11 +599,12 @@ class Mirror(Surface):
# couplings (for quantum noise calcs)
knm_loss(Knm, out=Knm_loss)
def _compute_knm_matrices(self, sim):
if not sim.is_modal or (not self._model._trace_changed and not self._angle_changed):
LOGGER.info("No knm dependent parameters have changed, skipping recomputation "
f"of knm matrices for: {self.name}")
return
def _compute_knm_matrices(self, sim, force=False):
if not force:
if not sim.is_modal or (not self._model._trace_changed and not self._angle_changed):
LOGGER.info("No knm dependent parameters have changed, skipping recomputation "
f"of knm matrices for: {self.name}")
return
LOGGER.info(f"Computing knm matrices for: {self.name}")
......
......@@ -267,12 +267,13 @@ class Modulator(Connector, FrequencyGenerator):
# zero the phase of TEM00 -> TEM00 coefficient
if sim.model.phase_config.ZERO_K00: zero_tem00_phase(Knm)
def _compute_knm_matrices(self, sim):
if not sim.is_modal or not self._model._trace_changed:
LOGGER.info("No knm dependent parameters have changed, "
"skipping recomputation of knm matrices for:"
f"{self.name}")
return
def _compute_knm_matrices(self, sim, force=False):
if not force:
if not sim.is_modal or not self._model._trace_changed:
LOGGER.info("No knm dependent parameters have changed, "
"skipping recomputation of knm matrices for:"
f"{self.name}")
return
LOGGER.info(f"Computing knm matrices for: {self.name}")
......
......@@ -5,70 +5,56 @@ Signal-type electrical component for producing signal inputs.
import numpy as np
from finesse.components.general import Connector
from finesse.components.node import NodeType, NodeDirection
from finesse.element import model_parameter, Rebuild
@model_parameter("amplitude", 1, Rebuild.NA, units="V")
@model_parameter("phase", 0, Rebuild.NA, units="degrees")
class SignalGenerator(Connector):
"""
An object which produces a signal with a given amplitude and phase.
"""
def __init__(self, name, node, amplitude=1, phase=0):
"""
Constructs a new `SignalGenerator` element which is used to inject signals into
a model. The
Constructs a new `SignalGenerator` element which is used to inject
signals into a model.
Parameters
----------
name : str
Name of the SignalGenerator instance.
node : .class:`finesse.components.node.Node`
A node to inject a signal into
amplitude : float, optional
Amplitude of the signal, units depends on the type of the `node` or `type` parameter
Amplitude of the signal, units depends on the type of the `node`
or `type` parameter
phase : float, optional
Phase-offset of the signal from the default, defaults to zero.
Phase-offset of the signal from the default in degrees,
defaults to zero.
"""
Connector.__init__(self, name)
self._add_port('port', node.type)
self.port._add_node('o', None, node)
self.__amplitude = amplitude
self.__phase = phase
self.amplitude = amplitude
self.phase = phase
@property
def amplitude(self):
"""The amplitude of the signal (in V).
:getter: Returns the signal amplitude.
:setter: Sets the signal amplitude.
"""
return self.__amplitude
@amplitude.setter
def amplitude(self, value):
self.__amplitude = value
@property
def phase(self):
"""The phase of the signal (in degrees).
:getter: Returns the signal phase.
:setter: Sets the signal phase.
"""
return self.__phase
@phase.setter
def phase(self, value):
self.__phase = value
def f(self):
return self._model.fsig.f
def _fill_rhs(self, sim):
if not sim.is_audio:
return
A = self.amplitude * np.exp(1j * self.phase * np.pi/180)
values, is_changing = self._eval_parameters()
amplitude = values["amplitude"]
phase = values["phase"]
A = amplitude * np.exp(1j * phase * np.pi / 180)
sim.M().set_rhs(sim.field(self.port.o), A)
......@@ -144,20 +144,26 @@ class Space(Connector):
forced_name=f'{base_conn_str}.p2.o')
def _check_L(self, value):
self._recompute_abcd = True
self._recompute_abcd = self._recompute_abcd.fromkeys(self._recompute_abcd, True)
if self.has_model:
self._model._retrace_required = True
return value
def _check_nr(self, value):
self._recompute_abcd = True
self._recompute_abcd = self._recompute_abcd.fromkeys(self._recompute_abcd, True)
if self.has_model:
self._model._retrace_required = True
if isinstance(self.p1.component, Surface):
self.p1.component._recompute_abcd = True
self.p1.component._recompute_abcd = self.p1.component._recompute_abcd.fromkeys(
self.p1.component._recompute_abcd, True
)
if isinstance(self.p2.component, Surface):
self.p2.component._recompute_abcd = True
self.p2.component._recompute_abcd = self.p2.component._recompute_abcd.fromkeys(
self.p2.component._recompute_abcd, True
)
return value
......@@ -228,9 +234,9 @@ class Space(Connector):
if symbolic:
raise NotImplementedError()
if not symbolic: