filterset_wheel.py 6.01 KB
Newer Older
Laurent Claustre's avatar
Laurent Claustre committed
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
93
94
95
96
97
98
99
100
101
# -*- 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 AutoFilterSetWheel serves to control the wheel filterset like model on ID10 
a motor driven wheel with up to 20 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: filtW0
  package: bliss.common.auto_filter.filterset_wheel
  class: FilterSet_Wheel
  rotation_axis: $att1
  filters:
    - name:Cu_0
      position: 0
      material: Cu
      thickness: 0
    - name:Cu_1
      position: 1
      material: Cu
      thickness: 0.04673608
    - name:Cu_2
      position: 2
      material: Cu
      thickness: 0.09415565
    - name:Cu_3
      position: 3
      material: Cu
      thickness: 0.14524267
    - name:Cu_4
      position: 4
      material: Cu
      thickness: 0.1911693
    - name:Cu_5
      position: 5
      material: Cu
      thickness: 0.24215921
    - name:Cu_6
      position: 6
      material: Cu
      thickness: 0.27220901
    - name:Cu_7
      position: 7
      material: Cu
      thickness: 0.3227842

With Density:
-------------
- name: filtW0
  package: bliss.common.auto_filter.filterset_wheel
  class: FilterSet_Wheel
  rotation_axis: $att1
  filters:
    - name:Cu_0
      position: 0
      material: Cu
      thickness: 0
      density: 8.94

    - name:Mo_1
      position: 1
      material: Mo
      thickness: 0.055
      density: 10.22

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

- name: filtW0
  package: bliss.common.auto_filter.filterset_wheel
  class: FilterSet_Wheel
  rotation_axis: $att1
  filters:
    - name:Ag_0
      position: 0
      material: Ag
      thickness: 0.1
      transmission: 0.173
      energy: 16

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

"""

102
import numpy as np
Laurent Claustre's avatar
Laurent Claustre committed
103
104

from bliss.common.auto_filter.filterset import FilterSet
105
from bliss.common.logtools import disable_user_output
Laurent Claustre's avatar
Laurent Claustre committed
106
107
108
109
110
111
112
113
114
115
116
117
118


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

        # check some config
        self._rotation_axis = config.get("rotation_axis")

        # never forget to call grandmother !!!
        super().__init__(name, config)

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

Laurent Claustre's avatar
Laurent Claustre committed
123
124
125
126
127
128
    def __info__(self):
        info_list = []
        info_list.append(f"Filterset Wheel: {self._name}")

        info_list.append(f" - Rotation axis: {self._rotation_axis.name}")
        info_list.append(
129
            f" - Idx   Pos. Mat. Thickness    Transm. @ {self._last_energy:.5g} keV:"
Laurent Claustre's avatar
Laurent Claustre committed
130
131
132
133
134
135
136
137
138
139
140
141
142
143
        )
        info_list.append("   -------------------------------------------------")
        for filter in self._filters:
            idx = self._filters.index(filter)
            info_list.append(
                f"   {idx:<4}  {filter['position']:<4} {filter['material']:<4} {filter['thickness']:10.8f}   {filter['transmission_calc']:.5g}"
            )
        return "\n".join(info_list) + "\n" + super().__info__()

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

144
    def set_filter(self, filter_id):
Laurent Claustre's avatar
Laurent Claustre committed
145
        """
146
        Set the new filter, for a wheel it move to a position
Laurent Claustre's avatar
Laurent Claustre committed
147
        """
148
        if filter_id not in range(len(self._filters)):
Laurent Claustre's avatar
Laurent Claustre committed
149
            raise ValueError(
150
                f"Wrong filter position {filter_id} supported values {self._positions}"
Laurent Claustre's avatar
Laurent Claustre committed
151
            )
152
        with disable_user_output():
153
            self._rotation_axis.move(self._filters[filter_id]["position"])
Laurent Claustre's avatar
Laurent Claustre committed
154
155
156

    def get_filter(self):
        """
157
158
        Return the wheel filter index.
        None is return if the axis position does not correspond to the 
Laurent Claustre's avatar
Laurent Claustre committed
159
160
161
162
163
164
165
        defined positions
        """
        position = self._rotation_axis.position
        if position in self._positions:
            filt = self._positions.index(position)
            return filt
        else:
166
167
168
            raise ValueError(
                f"The Filterset {self.name} motor ({self._rotation_axis.name}) position is {position:.4f}\n        Please move it to a filter position: {self._positions} "
            )
Laurent Claustre's avatar
Laurent Claustre committed
169
170
            return None

171
    def get_transmission(self, filter_id=None):
Laurent Claustre's avatar
Laurent Claustre committed
172
        """
173
174
        Return the tranmission of filter filter_id
        if None, return the curent filter transmission
Laurent Claustre's avatar
Laurent Claustre committed
175
        """
176
177
178
179
        if not filter_id:
            filt = self.get_filter()
        else:
            filt = filter_id
Laurent Claustre's avatar
Laurent Claustre committed
180
        if filt is not None:
181

Laurent Claustre's avatar
Laurent Claustre committed
182
183
184
185
            trans = self._filters[filt]["transmission_calc"]
        else:
            trans = None
        return trans
186
187
188

    def build_filterset(self):
        """
189
        Build pattern (index here)  and transmission arrays.
190
191
192
193
194
195
196
197
        A filterset, like Wago, is made of 4 real filters 
        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.
        """

        p = []
        t = []
198
        for filter in self._config_filters:
199
            # store just the index of the filters as the possible pattern
200
            p.append(self._config_filters.index(filter))
201
            t.append(filter["transmission_calc"])
202
        self._fpattern = np.array(p, dtype=int)
203
204
205
        self._ftransm = np.array(t)

        return len(self._fpattern)
206
207
208

    def get_filters(self):
        """
209
        Return the list of the public filters, a list of dictionary items with at least:
210
211
212
213
214
        - position
        - transmission_calc
        For the wheel filterset _filters = _config_filters
        """
        return self._config_filters