diff --git a/atip/simulator.py b/atip/simulator.py index 948e253..9dfdba2 100644 --- a/atip/simulator.py +++ b/atip/simulator.py @@ -1,8 +1,10 @@ """Module containing an interface with the AT simulator.""" import logging +import time from warnings import warn import at +from at.physics.orbit import find_orbit6 import numpy import cothread from scipy.constants import speed_of_light @@ -71,15 +73,21 @@ def __init__(self, at_lattice, callback=None, emit_calc=True): self._at_lat = at_lattice self._rp = numpy.ones(len(at_lattice) + 1, dtype=bool) self._emit_calc = emit_calc + self._at_lat.radiation_on() + self._orbit6, self._orbit6all = find_orbit6(self._at_lat, range(len(self._at_lat) + 1)) + print(self._orbit6) + print(self._orbit6.shape) # Initial phys data calculation. if self._emit_calc: - self._at_lat.radiation_on() - self._emitdata = self._at_lat.ohmi_envelope(self._rp) + self._emitdata = self._at_lat.ohmi_envelope( + self._rp, orbit=self._orbit6 + ) self._at_lat.radiation_off() self._lindata = self._at_lat.linopt(refpts=self._rp, get_chrom=True, coupled=False) self._radint = self._at_lat.get_radiation_integrals(0.0, self._lindata[3]) + self._at_lat.radiation_on() # Threading stuff initialisation. self._queue = cothread.EventQueue() # Explicitly manage the cothread Events, so turn off auto_reset. @@ -97,6 +105,7 @@ def queue_set(self, func, field, value): field (str): The field to be changed. value (float): The value to be set. """ + #logging.debug('adding {}:{} to the queue'.format(field, value)) self._queue.Signal((func, field, value)) def _gather_one_sample(self): @@ -109,7 +118,7 @@ def _gather_one_sample(self): def _recalculate_phys_data(self, callback): """Target function for the Cothread thread. Recalculates the physics - data dependant on the status of the '_paused' flag and the length of + data dependent on the status of the '_paused' flag and the length of the queue. The calculations only take place if '_paused' is False and there is one or more changes on the queue. @@ -128,24 +137,39 @@ def _recalculate_phys_data(self, callback): """ while True: logging.debug('Starting recalculation loop') + t = time.time() + # If there's no change on the queue wait for one to appear. self._gather_one_sample() + logging.debug('Gathered one sample after {:.3f} s'.format(time.time() - t)) + logging.debug('queue length {}'.format(len(self._queue))) while self._queue: self._gather_one_sample() + logging.debug('Gathered all samples after {:.3f} s'.format(time.time() - t)) if bool(self._paused) is False: try: + t = time.time() + logging.debug('Starting orbit calculation') + self._orbit6, self._orbit6all = find_orbit6(self._at_lat, range(len(self._at_lat) + 1)) + t = time.time() - t + logging.debug('Completed orbit calculation in {:.3f} seconds'.format(t)) + if self._emit_calc: + t = time.time() logging.debug('Starting emittance calculation.') - self._at_lat.radiation_on() - self._emitdata = self._at_lat.ohmi_envelope(self._rp) - logging.debug('Completed emittance calculation') + self._emitdata = self._at_lat.ohmi_envelope(self._rp, orbit=self._orbit6) + t = time.time() - t + logging.debug('Completed emittance calculation in {:.3f} seconds'.format(t)) logging.debug('Starting linear optics calculation.') + t = time.time() self._at_lat.radiation_off() self._lindata = self._at_lat.linopt(0.0, self._rp, True, coupled=False) - logging.debug('Completed linear optics calculation.') + t = time.time() - t + logging.debug('Completed linear optics calculation in {:.3f} seconds'.format(t)) self._radint = self._at_lat.get_radiation_integrals( twiss=self._lindata[3] ) + self._at_lat.radiation_on() logging.debug('All calculation complete.') except Exception as e: warn(at.AtWarning(e)) @@ -313,15 +337,15 @@ def get_orbit(self, field=None): FieldException: if the specified field is not valid for orbit. """ if field is None: - return self._lindata[3]['closed_orbit'][:-1] + return self._orbit6all[:-1] elif field == 'x': - return self._lindata[3]['closed_orbit'][:-1, 0] + return self._orbit6all[:-1, 0] elif field == 'px': - return self._lindata[3]['closed_orbit'][:-1, 1] + return self._orbit6all[:-1, 1] elif field == 'y': - return self._lindata[3]['closed_orbit'][:-1, 2] + return self._orbit6all[:-1, 2] elif field == 'py': - return self._lindata[3]['closed_orbit'][:-1, 3] + return self._orbit6all[:-1, 3] else: raise FieldException("Field {0} is not a valid closed orbit plane." .format(field)) diff --git a/virtac/atip_server.py b/virtac/atip_server.py index 05cde92..fb4768e 100644 --- a/virtac/atip_server.py +++ b/virtac/atip_server.py @@ -1,7 +1,10 @@ import csv +import logging +import time from warnings import warn import atip +import cothread import numpy import pytac from cothread.catools import camonitor @@ -89,6 +92,10 @@ def __init__(self, ring_mode, limits_csv=None, feedback_csv=None, self._create_mirror_records(mirror_csv) print("Finished creating all {0} records." .format(len(self.all_record_names))) + self._last_update = time.time() + self._gaps = [] + self._durations = [] + self._last_end = None @property def all_record_names(self): @@ -232,6 +239,9 @@ def _on_update(self, value, name): value (number): The value that has just been set to the record. name (str): The name of record object that has just been set to. """ + start = time.time() + if self._last_end is not None: + self._gaps.append(start - self._last_end) in_record = self._out_records[self.all_record_names[name]] in_record.set(value) index, field = self._in_records[in_record] @@ -242,6 +252,7 @@ def _on_update(self, value, name): except KeyError: pass if isinstance(index, list): + print('on update list name {} index {}'.format(name, index)) for i in index: self.lattice[i - 1].set_value(field, value, units=pytac.ENG, data_source=pytac.SIM) @@ -249,6 +260,26 @@ def _on_update(self, value, name): self.lattice[index - 1].set_value(field, value, units=pytac.ENG, data_source=pytac.SIM) + self._last_end = time.time() + self._durations.append(self._last_end - start) + if time.time() - self._last_update > 1: + logging.debug('Yielding.') + logging.debug('{} gaps {} durations'.format( + len(self._gaps), len(self._durations)) + ) + if self._gaps: + logging.debug( + 'av gap {}'.format(sum(self._gaps) / len(self._gaps)) + ) + self._gaps = [] + if self._durations: + logging.debug('av duration {}'.format( + sum(self._durations) / len(self._durations)) + ) + self._durations = [] + cothread.Yield() + self._last_update = time.time() + def _create_feedback_records(self, feedback_csv): """Create all the feedback records from the .csv file at the location passed, see create_csv.py for more information; records for two edge diff --git a/virtac/create_csv.py b/virtac/create_csv.py index 4b7f958..f272db5 100644 --- a/virtac/create_csv.py +++ b/virtac/create_csv.py @@ -110,6 +110,11 @@ def generate_mirrored_pvs(): """ lattice = atip.utils.loader() data = [("output type", "mirror type", "in", "out", "value")] + current = lattice.get_value('beam_current', pytac.RB, data_source=pytac.SIM) + data.append( + 'aIn', 'basic', 'SR-DI-DCCT-01:SIGNAL', 'SR23C-DI-DCCT-01:SIGNAL', current + ) + # Tune PV aliases. tune = [lattice.get_value('tune_x', pytac.RB, data_source=pytac.SIM), lattice.get_value('tune_y', pytac.RB, data_source=pytac.SIM)] @@ -185,7 +190,7 @@ def write_data_to_file(data, filename): if not filename.endswith('.csv'): filename += '.csv' here = os.path.abspath(os.path.dirname(__file__)) - with open(os.path.join(here, filename), "wb") as file: + with open(os.path.join(here, filename), "w") as file: csv_writer = csv.writer(file) csv_writer.writerows(data)