Commit 9b013833 authored by Daniel Brown's avatar Daniel Brown

Performance tuning. I've refactored a bunch of code for the eval_parameters....

Performance tuning. I've refactored a bunch of code for the eval_parameters. is_changing was taking up way to much CPU time. is_changing should be called in on_build and the result cached. This happens now in Element._setup_changing_params. is_changing is now a Set(Parameters). Simulations.any_frequencies_changing is now an attribute to save that getting recalled again and again. Shaved 0.7s off of a 2s simulation.
parent 21c6f7e0
......@@ -9,7 +9,7 @@ import math
import numpy as np
from finesse.exceptions import TotalReflectionError
from finesse.element import model_parameter, Rebuild, any_changing
from finesse.element import model_parameter, Rebuild
from finesse.knm import (
KnmMatrix,
compute_knm_matrix_bh,
......@@ -191,6 +191,7 @@ class Beamsplitter(Surface):
# -> need to fix this in general for all model parameters by only calling
# the validator when performing an analysis / scan
self._angle_changed = False
self.__changing_check = set((self.R, self.T, self.phi, self.alpha))
def _check_alpha(self, value):
self._cos_alpha = np.cos(np.radians(value))
......@@ -400,15 +401,14 @@ class Beamsplitter(Surface):
def _fill_matrix(self, sim):
values, is_changing = self._eval_parameters()
freqs_changing = any(freq.symbol.is_changing for freq in sim.frequencies)
if (
not sim.initial_filled[self]
or sim.is_audio
or self._model._trace_changed
or self._angle_changed
or freqs_changing
or any_changing(is_changing, params=["R", "T", "phi", "alpha"])
or sim.any_frequencies_changing
or (len(is_changing) and is_changing.issubset(self.__changing_check))
):
beamsplitter_fill(
self, sim, values, self._cos_alpha, self._cos_alpha_2, sim.model.lambda0
......
......@@ -78,7 +78,7 @@ class Isolator(Connector):
def _fill_matrix(self, sim):
ws = sim.ws[self]
values, is_changing = self._eval_parameters()
values, _ = self._eval_parameters()
suppression = 10 ** (-values["S"] / 20)
for freq in sim.frequencies:
......
......@@ -142,7 +142,7 @@ class Laser(Connector, FrequencyGenerator):
if not sim.is_audio:
return
values, is_changing = self._eval_parameters()
values, _ = self._eval_parameters()
laser_fill_signal(self, sim, values)
def _fill_rhs(self, sim):
......@@ -150,7 +150,7 @@ class Laser(Connector, FrequencyGenerator):
if sim.is_audio:
return # no RHS signal injections heres
values, is_changing = self._eval_parameters()
values, _ = self._eval_parameters()
fsrc = ws.fsrc
laser_fill_rhs(self, sim, values, fsrc.index)
......
......@@ -7,7 +7,7 @@ import logging
import numpy as np
from finesse.element import model_parameter, Rebuild, any_changing
from finesse.element import model_parameter, Rebuild
from finesse.knm import (
KnmMatrix,
compute_knm_matrix_bh,
......@@ -141,6 +141,8 @@ class Mirror(Surface):
self._register_node_coupling(self.mech.z, self.p1.o)
self._register_node_coupling(self.mech.z, self.p2.o)
self.__changing_check = set((self.R, self.T, self.phi))
def ABCD(self, from_node, to_node, direction="x", symbolic=False):
r"""Computes and returns the ABCD matrix of the mirror for
the specified coupling.
......@@ -289,15 +291,13 @@ class Mirror(Surface):
def _fill_matrix(self, sim):
values, is_changing = self._eval_parameters()
freqs_changing = any(freq.symbol.is_changing for freq in sim.frequencies)
if (
not sim.initial_filled[self]
or sim.is_audio
or self._model._trace_changed
or self._angle_changed
or freqs_changing
or any_changing(is_changing, params=["R", "T", "phi"])
or sim.any_frequencies_changing
or (len(is_changing) and is_changing.issubset(self.__changing_check))
):
mirror_fill(self, sim, values, sim.model.lambda0)
......
......@@ -9,7 +9,7 @@ import numpy as np
from finesse.components.general import InteractionType, Connector, FrequencyGenerator
from finesse.components.node import NodeType, NodeDirection
from finesse.components.matrixfill import modulator_fill, ModulatorWorkspace
from finesse.element import model_parameter, Rebuild, any_changing
from finesse.element import model_parameter, Rebuild
from finesse.knm import KnmMatrix, compute_knm_matrix_bh, rev_all_gouy, zero_tem00_phase
from finesse.utilities.maths import transform_q
from finesse.utilities import refractive_index
......@@ -90,6 +90,9 @@ class Modulator(Connector, FrequencyGenerator):
self.p2.i, self.p1.o, interaction_type=InteractionType.TRANSMISSION
)
self.__freqs_changing = None
self.__changing_check = set((self.midx, self.phase))
def _modulation_frequencies(self):
orders = list(range(-self.order, 1 + self.order))
orders.pop(self.order) # remove 0
......@@ -142,7 +145,6 @@ class Modulator(Connector, FrequencyGenerator):
def _on_build(self, sim):
ws = sim.ws[self] = ModulatorWorkspace()
ws.coupling_orders = {}
self._allocate_knm_matrices(sim)
for f1 in sim.frequencies:
......@@ -162,6 +164,7 @@ class Modulator(Connector, FrequencyGenerator):
def _on_unbuild(self, sim):
ws = sim.ws[self]
self.__freqs_changing = None
del ws.coupling_orders
def _update_source_frequency(self, sim, freq):
......@@ -186,12 +189,11 @@ class Modulator(Connector, FrequencyGenerator):
def _fill_matrix(self, sim):
ws = sim.ws[self]
values, is_changing = self._eval_parameters()
freqs_changing = any(freq.symbol.is_changing for freq in sim.frequencies)
if (
not sim.initial_filled[self]
or freqs_changing
or any_changing(is_changing, params=["midx", "phase"])
or sim.any_frequencies_changing
or (len(is_changing) and is_changing.issubset(self.__changing_check))
):
modulator_fill(self, sim, values, ws.coupling_orders, self.mod_type)
......
......@@ -45,7 +45,7 @@ class SignalGenerator(Connector):
if not sim.is_audio:
return
values, is_changing = self._eval_parameters()
values, _ = self._eval_parameters()
amplitude = values["amplitude"]
phase = values["phase"]
......
......@@ -5,7 +5,7 @@ Space-type objects representing physical distances between components.
import logging
import numpy as np
from finesse.element import model_parameter, Rebuild, any_changing
from finesse.element import model_parameter, Rebuild
from finesse.utilities.maths import transform_q
from finesse.utilities.maths import complex_equal
......@@ -85,6 +85,8 @@ class Space(Connector):
if portA is not None and portB is not None:
self.connect(portA, portB)
self.__changing_check = set((self.L, self.nr))
@property
def gouy_x(self):
return np.degrees(self._gouy_x)
......@@ -273,14 +275,13 @@ class Space(Connector):
def _fill_matrix(self, sim):
values, is_changing = self._eval_parameters()
freqs_changing = any(freq.symbol.is_changing for freq in sim.frequencies)
if (
not sim.initial_filled[self]
or sim.is_audio
or self._model._trace_changed
or freqs_changing
or any_changing(is_changing)
or sim.any_frequencies_changing
or (len(is_changing) and is_changing.issubset(self.__changing_check))
):
space_fill(
self,
......
......@@ -11,7 +11,7 @@ import numbers
import numpy as np
import logging
from finesse.element import model_parameter, Rebuild, any_changing
from finesse.element import model_parameter, Rebuild
from finesse.detectors.general import Detector
from finesse.detectors.compute import ccd_output, line_output, pixel_output
......
......@@ -69,38 +69,6 @@ def display(a):
return str(a)
def any_changing(is_changing, params=None):
"""Given a dictionary of `param : is_changing_flag` mappings (produced from
a call to the ``_eval_parameters`` method of a :class:`ModelElement`), determine whether any
parameters are changing.
A `param` filter can be specified to determine if anything only in this sequence is changing.
Parameters
----------
is_changing : dict
A dictionary of mappings from `parameter : is_changing_flag`.
params : sequence, optional
An optional sequence of parameters which is a subset of the parameters
in `is_changing.keys()`. Defaults to `None` so that all parameters in
`is_changing` are checked.
Returns
-------
flag : bool
A boolean value indicating whether any of the parameters are changing.
"""
if params is not None:
if not hasattr(params, "__getitem__"):
params = [params]
p_changing = {p: is_changing[p] for p in params}
else:
p_changing = is_changing
return any(p_changing.values())
class Symbol(object):
__add__ = MAKE_LOP(operator.add)
__sub__ = MAKE_LOP(operator.sub)
......@@ -778,18 +746,25 @@ class ModelElement(object):
def _reset_model(self, new_model):
self.__model = weakref.ref(new_model)
def _eval_parameters(self):
def _setup_changing_params(self):
# N.B. Decorators are evaluated from inside - out, so to make the order returned here match
# the order defined, we must reverse self.parameters
return (
{
p.name: (p.value.eval() if hasattr(p.value, "eval") else p.value)
for p in reversed(self.parameters)
},
{
p.name: (
p.value.is_changing if hasattr(p.value, "eval") else p.is_tunable
)
for p in reversed(self.parameters)
},
self._params_changing = set(
p for p in reversed(self.parameters) if p.is_changing
)
self._params_evald = {
p.name: (p.value.eval() if hasattr(p.value, "eval") else p.value)
for p in reversed(self.parameters)
}
def _clear_changing_params(self):
del self._params_changing
del self._params_evald
def _eval_parameters(self):
# Now we know which are evaluable so no need for repeated checks
# Also reduce memory bashing creating a ton of dictionaries and
# reuse just one.
for p in self._params_changing:
self._params_evald[p.name] = p.eval()
return self._params_evald, self._params_changing
......@@ -92,6 +92,7 @@ cdef class BaseSimulation:
self.frequency_map = None
self.nhoms = 1
self.ws = {}
self.any_frequencies_changing = True
# register that this simulation is dependant on this model
model._register_simulation(self)
......@@ -568,6 +569,11 @@ cdef class BaseSimulation:
self.frequencies = frequencies
#self.frequencies = np.array(frequencies, dtype=object)
tmp = []
# cpdef doesn't like generators so have to spell this out with a for-loop
for _ in self.frequencies:
tmp.append(_.symbol.is_changing )
self.any_frequencies_changing = any(tmp)
self._Nf = len(self.frequencies)
cpdef _initialise_nodes(self):
......@@ -644,6 +650,8 @@ cdef class BaseSimulation:
if hasattr(el, "_update_tem_gouy_phases"):
self._set_gouy_phases.append(el._update_tem_gouy_phases)
el._setup_changing_params()
if hasattr(el, "_on_build"):
el._on_build(self)
......@@ -671,6 +679,7 @@ cdef class BaseSimulation:
for el in self.model.elements.values():
if hasattr(el, "_on_unbuild"):
el._on_unbuild(self)
el._clear_changing_params()
def __exit__(self, type_, value, traceback):
raise NotImplementedError
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment