Commit 1d15851c authored by Daniel Brown's avatar Daniel Brown
Browse files

Merge branch 'merging_carrier_signal_sim' into 'master'

Merging carrier and signal simulation

See merge request !49
parents fa1db621 38683d8b
...@@ -46,24 +46,25 @@ class MicroResonator(Connector): ...@@ -46,24 +46,25 @@ class MicroResonator(Connector):
self.mech.F_z.frequencies = self.frequencies self.mech.F_z.frequencies = self.frequencies
def _get_workspace(self, sim): def _get_workspace(self, sim):
if sim.is_audio: if sim.carrier.any_frequencies_changing:
# ddb - This case probably needs some more thought on what beatings change
raise NotImplementedError(
"Changing carrier frequencies whilst using a MicroResonant not supported yet."
)
if sim.signal:
refill = ( refill = (
sim.model.fsig.f.is_changing sim.model.fsig.f.is_changing
or self.mass.is_changing or self.mass.is_changing
or self.f.is_changing or self.f.is_changing
or self.Q.is_changing or self.Q.is_changing
) )
ws = MicroResonatorWorkspace(self, sim, refill) ws = MicroResonatorWorkspace(self, sim, refill, refill)
ws.set_fill_fn(self.fill) ws.signal.set_fill_function(self.fill)
ws.Fz_frequencies = sim.mechanical_frequencies[self.mech.F_z] ws.Fz_frequencies = sim.signal.mechanical_frequencies[self.mech.F_z]
ws.z_frequencies = sim.mechanical_frequencies[self.mech.z] ws.z_frequencies = sim.signal.mechanical_frequencies[self.mech.z]
return ws return ws
else:
if sim.any_frequencies_changing:
# ddb - This case probably needs some more thought on what beatings change
raise NotImplementedError(
"Changing carrier frequencies whilst using a MicroResonant not supported yet."
)
return None return None
def _couples_frequency(self, ws, connection, frequency_in, frequency_out): def _couples_frequency(self, ws, connection, frequency_in, frequency_out):
...@@ -88,7 +89,7 @@ class MicroResonator(Connector): ...@@ -88,7 +89,7 @@ class MicroResonator(Connector):
for fi in ws.Fz_frequencies.frequencies: for fi in ws.Fz_frequencies.frequencies:
for fo in ws.z_frequencies.frequencies: for fo in ws.z_frequencies.frequencies:
if fi.f == fo.f: if fi.f == fo.f:
with ws.sim.component_edge_fill3( with ws.sim.signal.component_edge_fill3(
ws.owner_id, ws.connections.F_2_Z_idx, fi.index, fo.index ws.owner_id, ws.connections.F_2_Z_idx, fi.index, fo.index
) as mat: ) as mat:
mat[:] = Hz(2 * np.pi * fo.f) mat[:] = Hz(2 * np.pi * fo.f)
......
...@@ -40,9 +40,10 @@ def get_sweep_array(start: float, stop: float, steps: int, mode="lin"): ...@@ -40,9 +40,10 @@ def get_sweep_array(start: float, stop: float, steps: int, mode="lin"):
class ActionWorkspace: class ActionWorkspace:
def __init__(self, s_prev, model): def __init__(self, s_prev, sim):
self.s_prev = s_prev self.s_prev = s_prev
self.model = model self.sim = sim
self.model = sim.model
self.fn_do = None self.fn_do = None
...@@ -89,9 +90,9 @@ class Action: ...@@ -89,9 +90,9 @@ class Action:
def fill_info(self, p_info): def fill_info(self, p_info):
p_info.add(self.copy_info()) p_info.add(self.copy_info())
def setup(self, s_prev, model): def setup(self, s_prev, sim):
# By default simple actions should just run their 'do' method # By default simple actions should just run their 'do' method
ws = ActionWorkspace(s_prev, model) ws = ActionWorkspace(s_prev, sim)
ws.fn_do = self.do ws.fn_do = self.do
return ws return ws
...@@ -112,10 +113,10 @@ class Action: ...@@ -112,10 +113,10 @@ class Action:
p.is_tunable = True p.is_tunable = True
initial_param_values[p] = p.value initial_param_values[p] = p.value
with model.built(): with model.built() as sim:
try: try:
s = BaseSolution(None, None) s = BaseSolution(None, None)
ws = self.setup(s, model) ws = self.setup(s, sim)
ws.fn_do(ws) ws.fn_do(ws)
except StopIteration: except StopIteration:
raise Exception("Should we reach this point? Probably unexpected") raise Exception("Should we reach this point? Probably unexpected")
...@@ -185,7 +186,6 @@ class ABCD(Action): ...@@ -185,7 +186,6 @@ class ABCD(Action):
class StepParamNDWorkspace(ActionWorkspace): class StepParamNDWorkspace(ActionWorkspace):
def __init__(self):
pass pass
...@@ -227,47 +227,37 @@ class StepParamND(Action): ...@@ -227,47 +227,37 @@ class StepParamND(Action):
if self.on_complete: if self.on_complete:
Folder("on_complete", self.on_complete).fill_info(info) Folder("on_complete", self.on_complete).fill_info(info)
def setup(self, s_prev, model: finesse.model.Model): def setup(self, s_prev, sim):
ws = StepParamNDWorkspace() ws = StepParamNDWorkspace(s_prev, sim)
ws.s_prev = s_prev
ws.model = model
ws.info = self.copy_info() ws.info = self.copy_info()
ws.fn_do = self.do ws.fn_do = self.do
ws.params = tuple(get_param(model, p) for p in self._info.parameters_changing) ws.params = tuple(get_param(ws.model, p) for p in self._info.parameters_changing)
for p in ws.params: for p in ws.params:
if not p.is_tunable: if not p.is_tunable:
raise ParameterLocked( raise ParameterLocked(
f"{repr(p)} must set as tunable " "before building the simulation" f"{repr(p)} must set as tunable " "before building the simulation"
) )
ws.carrier = model.carrier_simulation
try:
ws.signal = model.signal_simulation
except AttributeError:
ws.signal = None
return ws return ws
def do(self, ws: StepParamNDWorkspace): def do(self, ws: StepParamNDWorkspace):
ws.sol = ArraySolution( ws.sol = ArraySolution(
self.name, ws.s_prev, ws.model, self.out_shape, self.axes, ws.params self.name, ws.s_prev, ws.sim.detector_workspaces, self.out_shape, self.axes, ws.params
) )
if self.pre_step: if self.pre_step:
ws.pre_step = Folder("pre_step", self.pre_step).setup(ws.sol, ws.model) ws.pre_step = Folder("pre_step", self.pre_step).setup(ws.sol, ws.sim)
else: else:
ws.pre_step = None ws.pre_step = None
if self.post_step: if self.post_step:
ws.post_step = Folder("post_step", self.post_step).setup(ws.sol, ws.model) ws.post_step = Folder("post_step", self.post_step).setup(ws.sol, ws.sim)
else: else:
ws.post_step = None ws.post_step = None
# Now we loop over the actual simulation and run each point # Now we loop over the actual simulation and run each point
run_axes_scan( run_axes_scan(
ws.carrier, ws.sim,
ws.signal,
self.axes, self.axes,
ws.params, ws.params,
self.offsets, self.offsets,
...@@ -278,7 +268,7 @@ class StepParamND(Action): ...@@ -278,7 +268,7 @@ class StepParamND(Action):
) )
if self.on_complete: if self.on_complete:
ws = Folder("on_complete", self.on_complete).setup(ws.sol, ws.model) ws = Folder("on_complete", self.on_complete).setup(ws.sol, ws.sim)
ws.fn_do(ws) ws.fn_do(ws)
...@@ -319,30 +309,33 @@ class XNaxis(StepParamND): ...@@ -319,30 +309,33 @@ class XNaxis(StepParamND):
on_complete=on_complete, on_complete=on_complete,
) )
def setup(self, s_prev, model): def setup(self, s_prev, sim):
# If the model has locks, set them up to happen on the # If the model has locks, set them up to happen on the
# pre-step of StepParamND action # pre-step of StepParamND action
if len(model.locks) > 0: if len(sim.model.locks) > 0:
self.pre_step = RunLocks(*model.locks) self.pre_step = RunLocks(*sim.model.locks)
return super().setup(s_prev, model) return super().setup(s_prev, sim)
def do(self, ws: StepParamNDWorkspace): def do(self, ws: StepParamNDWorkspace):
model = ws.model.deepcopy() model = ws.sim.model.deepcopy()
params = ( params = (
get_param(model, pstr) for pstr in ws.info.get_all_parameters_changing() get_param(model, pstr) for pstr in ws.info.get_all_parameters_changing()
) )
try:
for p in params: for p in params:
p.is_tunable = True p.is_tunable = True
with model.built(): with model.built():
super().do(ws) super().do(ws)
finally:
for p in params: for p in params:
p.is_tunable = False p.is_tunable = False
def __getattr__(self, key): def __getattr__(self, key):
res = re.match("(parameter|mode|start|stop|steps|offset)([0-9]*)", key) res = re.match("(parameter|mode|start|stop|steps|offset)([0-9]*)", key)
if res is None: if res is None:
...@@ -524,15 +517,15 @@ class Serial(Action): ...@@ -524,15 +517,15 @@ class Serial(Action):
if len(p_info.children) == 0: if len(p_info.children) == 0:
raise Exception("Analysis information object should have been made") raise Exception("Analysis information object should have been made")
def setup(self, s_prev, model): def setup(self, s_prev, sim):
ws = ActionWorkspace(s_prev, model) ws = ActionWorkspace(s_prev, sim)
ws.wss = [] ws.wss = []
ws.fn_do = self.do ws.fn_do = self.do
curr_children = len(ws.s_prev.children) curr_children = len(ws.s_prev.children)
# Here we get workspaces for each of the # Here we get workspaces for each of the
# actions we need to run # actions we need to run
for arg in self.args: for arg in self.args:
ws.wss.append(arg.setup(s_prev, model)) ws.wss.append(arg.setup(s_prev, sim))
if len(ws.s_prev.children) > curr_children: if len(ws.s_prev.children) > curr_children:
# If a solution was made and added as a child in the previous coroutine # If a solution was made and added as a child in the previous coroutine
# then that becomes the next solution in the serial chain # then that becomes the next solution in the serial chain
...@@ -556,10 +549,10 @@ class Folder(Action): ...@@ -556,10 +549,10 @@ class Folder(Action):
self.action.fill_info(info) self.action.fill_info(info)
self.folder = None self.folder = None
def setup(self, s_prev, model): def setup(self, s_prev, sim):
ws = ActionWorkspace(s_prev, model) ws = ActionWorkspace(s_prev, sim)
ws.folder = BaseSolution(self.name, ws.s_prev) ws.folder = BaseSolution(self.name, ws.s_prev)
ws.action_ws = self.action.setup(ws.folder, model) ws.action_ws = self.action.setup(ws.folder, sim)
ws.fn_do = self.do ws.fn_do = self.do
return ws return ws
...@@ -704,24 +697,23 @@ class RunLocks(Action): ...@@ -704,24 +697,23 @@ class RunLocks(Action):
def fill_info(self, p_info): def fill_info(self, p_info):
p_info.add(self.copy_info()) p_info.add(self.copy_info())
def setup(self, s_prev, model: finesse.model.Model): def setup(self, s_prev, sim):
ws = RunLocksWorkspace(s_prev, model) ws = RunLocksWorkspace(s_prev, sim)
ws.locks = tuple(model.elements[l] for l in self.locks) ws.locks = tuple(sim.model.elements[l] for l in self.locks)
ws.det_ws = [None,] * self.num_locks ws.det_ws = [None,] * self.num_locks
for i, l in enumerate(ws.locks): for i, l in enumerate(ws.locks):
for j, d in enumerate(model.detectors): for j, d in enumerate(sim.model.detectors):
if l.error_signal.name == d.name: if l.error_signal.name == d.name:
ws.det_ws[i] = model._detector_workspaces[j] ws.det_ws[i] = sim.detector_workspaces[j]
if any(_ is None for _ in ws.det_ws): if any(_ is None for _ in ws.det_ws):
raise Exception("Could not find detector workspaces for all locks") raise Exception("Could not find detector workspaces for all locks")
ws.info = self.copy_info() ws.info = self.copy_info()
ws.s_prev = s_prev ws.s_prev = s_prev
ws.sim = model.carrier_simulation
ws.fn_do = do_lock ws.fn_do = do_lock
ws.params = tuple(get_param(model, p) for p in self._info.parameters_changing) ws.params = tuple(get_param(sim.model, p) for p in self._info.parameters_changing)
ws.max_iterations = self.max_iterations ws.max_iterations = self.max_iterations
for p in ws.params: for p in ws.params:
...@@ -737,7 +729,7 @@ def do_lock(ws: RunLocksWorkspace): ...@@ -737,7 +729,7 @@ def do_lock(ws: RunLocksWorkspace):
iters = 0 iters = 0
while recompute and iters < ws.max_iterations: while recompute and iters < ws.max_iterations:
iters += 1 iters += 1
ws.sim.run(True) ws.sim.run_carrier()
recompute = False recompute = False
for i, dws in enumerate(ws.det_ws): for i, dws in enumerate(ws.det_ws):
acc = ws.locks[i].accuracy acc = ws.locks[i].accuracy
......
...@@ -8,7 +8,7 @@ from cpython.ref cimport PyObject ...@@ -8,7 +8,7 @@ from cpython.ref cimport PyObject
from finesse.cymath cimport complex_t from finesse.cymath cimport complex_t
from finesse.parameter cimport Parameter from finesse.parameter cimport Parameter
from finesse.simulations cimport BaseSimulation from finesse.simulations.basematrix cimport CarrierSignalMatrixSimulation
from finesse.solutions.array import ArraySolution from finesse.solutions.array import ArraySolution
from finesse.solutions.array cimport ArraySolution from finesse.solutions.array cimport ArraySolution
...@@ -18,8 +18,7 @@ LOGGER = logging.getLogger(__name__) ...@@ -18,8 +18,7 @@ LOGGER = logging.getLogger(__name__)
@cython.wraparound(False) @cython.wraparound(False)
@cython.initializedcheck(False) @cython.initializedcheck(False)
cpdef run_fsig_sweep( cpdef run_fsig_sweep(
BaseSimulation carrier, CarrierSignalMatrixSimulation sim,
BaseSimulation signal,
double[::1] axis, double[::1] axis,
long[::1] input_rhs_indices, long[::1] input_rhs_indices,
long[::1] output_rhs_indices, long[::1] output_rhs_indices,
...@@ -39,7 +38,7 @@ cpdef run_fsig_sweep( ...@@ -39,7 +38,7 @@ cpdef run_fsig_sweep(
int No = len(output_rhs_indices) int No = len(output_rhs_indices)
int i, o, j int i, o, j
complex_t denom complex_t denom
Parameter f = signal.model.fsig.f Parameter f = sim.model.fsig.f
if out is None: if out is None:
out = np.zeros((Na, Ni, No), dtype=np.complex_128) out = np.zeros((Na, Ni, No), dtype=np.complex_128)
...@@ -48,36 +47,39 @@ cpdef run_fsig_sweep( ...@@ -48,36 +47,39 @@ cpdef run_fsig_sweep(
assert(out.shape[1] == Na) assert(out.shape[1] == Na)
assert(out.shape[2] == No) assert(out.shape[2] == No)
# We'll be making our own RHS inputs for this simulation
sim.signal.manual_rhs = True
for i in range(Ni): for i in range(Ni):
for j in range(Na): for j in range(Na):
f.set_double_value(axis[j]) f.set_double_value(axis[j])
signal.model_data.fsig = axis[j] sim.signal.model_data.fsig = axis[j]
signal._clear_rhs() sim.signal.clear_rhs()
signal.set_source_fast_2( sim.signal.set_source_fast_2(
input_rhs_indices[i], 1 input_rhs_indices[i], 1
) )
signal.run(False) sim.signal.run()
if not compute_open_loop: if not compute_open_loop:
for o in range(No): for o in range(No):
out[i][j][o] = signal.out_view[output_rhs_indices[o]] out[i][j][o] = sim.signal.out_view[output_rhs_indices[o]]
else: else:
for o in range(No): for o in range(No):
# We can divide out the 1/(1-H) closed loop behaviours by # We can divide out the 1/(1-H) closed loop behaviours by
# using the coupling computed back into the same input node # using the coupling computed back into the same input node
denom = signal.out_view[input_rhs_indices[i]] denom = sim.signal.out_view[input_rhs_indices[i]]
if denom.real == denom.imag == 0: if denom.real == denom.imag == 0:
out[i][j][o] = 0 out[i][j][o] = 0
else: else:
out[i][j][o] = signal.out_view[output_rhs_indices[o]] / denom out[i][j][o] = sim.signal.out_view[output_rhs_indices[o]] / denom
sim.signal.manual_rhs = False
return out return out
@cython.boundscheck(False) @cython.boundscheck(False)
@cython.wraparound(False) @cython.wraparound(False)
@cython.initializedcheck(False) @cython.initializedcheck(False)
def run_axes_scan( def run_axes_scan(
BaseSimulation carrier, CarrierSignalMatrixSimulation sim,
BaseSimulation signal,
tuple axes, tuple axes,
tuple params, tuple params,
double[:] offsets, double[:] offsets,
...@@ -151,11 +153,10 @@ def run_axes_scan( ...@@ -151,11 +153,10 @@ def run_axes_scan(
# ------------------------------------------------------ # ------------------------------------------------------
# DO STEP # DO STEP
# ------------------------------------------------------ # ------------------------------------------------------
mask_this_point = carrier.run(True) mask_this_point = sim.run_carrier()
if not mask_this_point and signal is not None: if not mask_this_point and sim.signal:
signal.model_data.fsig = signal.model.fsig.f.value sim.run_signal()
mask_this_point = signal.run(True)
# ------------------------------------------------------ # ------------------------------------------------------
# POST STEP # POST STEP
# ------------------------------------------------------ # ------------------------------------------------------
......
...@@ -586,16 +586,17 @@ class Beamsplitter(Surface): ...@@ -586,16 +586,17 @@ class Beamsplitter(Surface):
def _get_workspace(self, sim): def _get_workspace(self, sim):
from finesse.components.modal.beamsplitter import ( from finesse.components.modal.beamsplitter import (
beamsplitter_fill, beamsplitter_carrier_fill,
beamsplitter_signal_fill,
BeamsplitterWorkspace, BeamsplitterWorkspace,
) )
_, is_changing = self._eval_parameters() _, is_changing = self._eval_parameters()
refill = ( refill = (
(sim.is_audio and sim.model.fsig.f.is_changing) (sim.signal and sim.model.fsig.f.is_changing)
or self in sim.trace_forest.changing_components or self in sim.trace_forest.changing_components
or sim.any_frequencies_changing or sim.carrier.any_frequencies_changing
or (len(is_changing) and is_changing.issubset(self.__changing_check)) or (len(is_changing) and is_changing.issubset(self.__changing_check))
) )
...@@ -604,7 +605,8 @@ class Beamsplitter(Surface): ...@@ -604,7 +605,8 @@ class Beamsplitter(Surface):
ws.nr1 = refractive_index(self.p1) or refractive_index(self.p2) or 1 ws.nr1 = refractive_index(self.p1) or refractive_index(self.p2) or 1
ws.nr2 = refractive_index(self.p3) or refractive_index(self.p4) or 1 ws.nr2 = refractive_index(self.p3) or refractive_index(self.p4) or 1
ws.set_fill_fn(beamsplitter_fill) ws.carrier.set_fill_function(beamsplitter_carrier_fill)
ws.signal.set_fill_function(beamsplitter_signal_fill)
# Initialise the ABCD matrix memory-views # Initialise the ABCD matrix memory-views
if sim.is_modal: if sim.is_modal:
...@@ -651,23 +653,3 @@ class Beamsplitter(Surface): ...@@ -651,23 +653,3 @@ class Beamsplitter(Surface):
ws.abcd_p4p2_y = self.ABCD(self.p4.i, self.p2.o, "y", copy=False) ws.abcd_p4p2_y = self.ABCD(self.p4.i, self.p2.o, "y", copy=False)
return ws return ws
\ No newline at end of file
def _fill_qnoise_rhs(self, sim):
ws = sim.ws[self]
if sim.is_modal:
for freq in sim.frequencies:
for k in range(sim.nhoms):
L1 = self.R * ws.K21_loss[k] + self.T * ws.K31_loss[k]
L2 = self.R * ws.K12_loss[