filterset_wago.py 7.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# -*- coding: utf-8 -*-
#
# This file is part of the bliss project
#
# Copyright (c) 2020 Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.


"""
Class FilterSet_Wago serves to control the DEG (former ISG) Wago filterset. 
Pneumatic system is driving 4 slots for attenuating the beam intensity.

Filters can be configured with only material and thickness then the density will be the theoric one
othewise one can set an density (g/cm3) or a pair of transmission(range [0-1]) / energy (keV).

Example of yml configuration files:

With NO density:
---------------

- name: filtA
  package: bliss.common.auto_filter.filterset_wago
  class: FilterSet_Wago
  wago_controller: $wcid10f
  wago_cmd: filtA
  wago_status: fstatA
  inverted: True
  overlap_time: 0.1
  settle_time: 0.3
  filters:
    - position: 0
      material: Cu
      thickness: 0
    - position: 1
      material: Cu
      thickness: 0.04673608
    - position: 2
      material: Cu
      thickness: 0.09415565
    - position: 3
      material: Cu
      thickness: 0.14524267

With Density:
-------------
- name: filtA
  package: bliss.common.auto_filter.filterset_wago
  class: FilterSet_Wago
  wago_controller: $wcid10f
  wago_cmd: filtA
  wago_status: fstatA
  inverted: True
  overlap_time: 0.1
  settle_time: 0.3
  filters:
    - position: 0
      material: Cu
      thickness: 0
      density: 8.94
    - position: 1
      material: Mo
      thickness: 0.055
      density: 10.22

With pairs transmission/energy:
------------------------------

- name: filtA
  package: bliss.common.auto_filter.filterset_wago
  class: FilterSet_Wago
  wago_controller: $wcid10f
  wago_cmd: filtA
  wago_status: fstatA
  inverted: True
  overlap_time: 0.1
  settle_time: 0.3
  filters:
    - position: 0
      material: Ag
      thickness: 0.1
      transmission: 0.173
      energy: 16

    - position: 1
      material: Ag
      thickness: 0.2
      transmission: 0.0412
      energy: 16

"""

import numpy as np
93
from gevent import sleep
94
95
96
97
98
99
100
101
102
103
104
105
106
107

from bliss.common.auto_filter.filterset import FilterSet


class FilterSet_Wago(FilterSet):
    def __init__(self, name, config):
        self._config = config
        self._name = name

        # check some config
        self._wago = config.get("wago_controller")
        self._wago_cmd = config.get("wago_cmd")
        self._wago_status = config.get("wago_status")

108
109
110
111
112
        # optional config
        self._inverted = config.get("inverted", False)
        self._overlap_time = config.get("overlap_time", 0)
        self._settle_time = config.get("settle_time", 0)

113
114
115
116
117
118
119
120
121
122
123
        # never forget to call grandmother !!!
        super().__init__(name, config)

        self._positions = []
        for filter in self._filters:
            self._positions.append(filter["position"])

    def __info__(self):
        info_list = []
        info_list.append(f"Filterset Wago: {self._name}")

124
        info_list.append(f" - Wago: {self._wago.name}")
125
        info_list.append(
126
            f" - Idx   Mat. Thickness    Transm. @ {self._last_energy:.5g} keV:"
127
        )
128
129
130
        info_list.append("   ---------------------------------------------")
        for filter in self._config_filters:
            idx = self._config_filters.index(filter)
131
            info_list.append(
132
                f"   {idx:<4}  {filter['material']:<4} {filter['thickness']:10.8f}   {filter['transmission_calc']:.5g}"
133
            )
134
135
136
137
138
139
140
141

        info_list.append("\n\n Combined filter set:")
        info_list.append(f" - Idx  Transm. @ {self._last_energy:.5g} keV:")
        info_list.append("   --------------------------")
        for filter in self._filters:
            idx = self._filters.index(filter)
            info_list.append(f"   {idx:<4} {filter['transmission_calc']:.5g}")

142
143
144
145
146
147
148
149
150
151
152
        return "\n".join(info_list) + "\n" + super().__info__()

    # --------------------------------------------------------------
    # Here start the mother class overloaded methods to create
    # a new filterset
    # --------------------------------------------------------------

    def set_filter(self, filter_id):
        """
        Set the new filter, for the wago filter_id is a mask
        """
153
        if filter_id < 0 or filter_id > self._filtmask:
154
            raise ValueError(
155
                f"Wrong filter value {filter_id} range is [0-{self._filtmask}]"
156
            )
157

158
        nbits = self._config_nb_filters
159
160
161
162
163
164
165
166
167
168
169
170

        # do not let 0 filter to be set otherwise detector
        # can be damaged, so first set on new filters by
        # making a logic OR between previous and new mask

        mask = self.get_filter() | filter_id
        mask = (self._filtmask - mask) if self._inverted else mask
        wmask = [int(x) for x in f"{mask:0{nbits}b}"]
        self._wago.set(self._wago_cmd, wmask)
        sleep(self._overlap_time)

        mask = (self._filtmask - filter_id) if self._inverted else filter_id
171
        wmask = [int(x) for x in f"{mask:0{nbits}b}"]
172
        wmask.reverse()
173
        self._wago.set(self._wago_cmd, wmask)
174
        sleep(self._settle_time)
175
176
177
178
179
180

    def get_filter(self):
        """
        Return the wago filter mask.
        """
        mask = 0
181
182
        val = self._wago.get(self._wago_status)
        for f in range(self._config_nb_filters):
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
            mask += int(val[f]) << f
        return mask

    def get_transmission(self, filter_id=None):
        """
        Return the tranmission of filter filter_id
        if None, return the curent filter transmission
        """
        if not filter_id:
            filt = self.get_filter()
        else:
            filt = filter_id
        if filt is not None:

            trans = self._filters[filt]["transmission_calc"]
        else:
            trans = None
        return trans

    def build_filterset(self):
        """
        Build pattern (index here)  and transmission arrays.
205
        A filterset, like Wago, is made of 4 real filters (or more)
206
207
208
209
210
        which can be combined to produce 15 patterns and transmissions.
        A filtersets like a wheel just provides 20 real filters and exactly
        the same amount of patterns and transmissions.
        """

211
212
        self._config_nb_filters = len(self._config_filters)

213
214
215
216
217
218
219
220
221
222
        # make a mask in case of inverted filterset
        self._filtmask = pow(2, self._config_nb_filters) - 1

        nbits = self._config_nb_filters
        # filter id 0 is no filter
        p = [0]
        t = [1.]
        self._built_filters = [{"position": 0, "transmission_calc": 1.}]

        for filt_id in range(1, self._filtmask + 1):
223
            # store just the index of the filters as the possible pattern
224
225
226
227
228
229
230
231
232
233
234
235
236
237
            p.append(filt_id)
            mask = [int(x) for x in f"{filt_id:0{nbits}b}"]
            mask.reverse()
            trans = 1.

            # TODO : optimize to loop only on the effective bits
            for f in range(nbits):
                if int(mask[f]) == 1:
                    trans *= self._config_filters[f]["transmission_calc"]

            t.append(trans)
            self._built_filters.append(
                {"position": filt_id, "transmission_calc": trans}
            )
238
        self._fpattern = np.array(p, dtype=int)
239
240
241
242
        self._ftransm = np.array(t)
        return len(self._fpattern)

    def get_filters(self):
243
        """
244
        Return the list of the public filters, a list of dictionary items with at least:
245
246
247
248
        - position
        - transmission_calc
        """
        return self._built_filters