Coverage for yaptide/converter/converter/fluka/cards/scoring_card.py: 95%
87 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-07-01 12:55 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-07-01 12:55 +0000
1from dataclasses import dataclass, field
2from typing import Optional, Union
4from converter.fluka.cards.card import Card
5from converter.fluka.helper_parsers.detector_parser import CylinderDetector, MeshDetector
6from converter.fluka.helper_parsers.scoring_parser import CustomFilter, ParticleFilter, Quantity, Scoring
8_DetectorType = type(Union[MeshDetector, CylinderDetector])
11@dataclass
12class ScoringCardIndexCounter:
13 """Class representing counter for Fluka scoring cards"""
15 usrbin_counter: int = 0
18def handle_scoring_cards(output_unit: int, scoring: Scoring, counter: ScoringCardIndexCounter) -> str:
19 """Creates Scoring cards"""
20 output: list[str] = []
21 for quantity in scoring.quantities:
22 usrbin_card = handle_usrbin_scoring(scoring.detector, quantity, output_unit, counter)
23 output.append(usrbin_card)
24 return '\n'.join(output).strip()
27def short_name(name: str) -> str:
28 """Creates short name for fluka card"""
29 return name[:10]
32def handle_usrbin_scoring(detector: _DetectorType, quantity: Quantity, output_unit,
33 counter: ScoringCardIndexCounter) -> str:
34 """Creates USRBIN card"""
35 output: list[Card] = []
36 # temporary assumption
37 # DOSE according to:
38 # https://flukafiles.web.cern.ch/manual/chapters/particle_and_material_codes/particles_codes.html
39 quantity_to_score = ''
40 try_auxscore = False
41 if quantity.keyword == 'Dose':
42 quantity_to_score = 'DOSE'
43 try_auxscore = True
44 elif quantity.keyword == 'Fluence':
45 if isinstance(quantity.scoring_filter, ParticleFilter):
46 # Apply particle from filter if fluency is used
47 quantity_to_score = quantity.scoring_filter.particle
48 else:
49 quantity_to_score = 'ALL-PART'
50 if isinstance(quantity.scoring_filter, CustomFilter):
51 try_auxscore = True
53 if not quantity_to_score:
54 return f'* unable to create USRBIN card for {quantity.name[:20]}'
56 output_unit_in_fluka_convention = str(output_unit * -1)
58 output.extend(parse_detector(detector, quantity, quantity_to_score, output_unit_in_fluka_convention))
60 counter.usrbin_counter += 1
61 if try_auxscore and quantity.scoring_filter:
62 # Add AUXSCORE card for custom filter
63 auxscore_card = handle_auxscore_filter(quantity, counter.usrbin_counter, 'USRBIN')
64 if auxscore_card:
65 output.append(auxscore_card)
67 return '\n'.join([f'{card!s}' for card in output])
70def parse_detector(detector, quantity, quantity_to_score, output_unit_in_fluka_convention) -> list[Card]:
71 """Creates USRBIN card"""
72 if isinstance(detector, CylinderDetector):
73 return _parse_cylinder_detector(detector, quantity, quantity_to_score,
74 output_unit_in_fluka_convention)
75 return _parse_mesh_detector(detector, quantity, quantity_to_score, output_unit_in_fluka_convention)
78def _parse_mesh_detector(detector: MeshDetector, quantity: Quantity, quantity_to_score: str,
79 output_unit_in_fluka_convention: str) -> list[Card]:
80 """Creates USRBIN card for mesh detector"""
81 first_card = Card(codewd='USRBIN')
82 first_card.what = [
83 '10.0', quantity_to_score, output_unit_in_fluka_convention, detector.x_max, detector.y_max, detector.z_max
84 ]
85 first_card.sdum = short_name(quantity.name)
87 second_card = Card(codewd='USRBIN')
88 second_card.what = [
89 detector.x_min,
90 detector.y_min,
91 detector.z_min,
92 detector.x_bins,
93 detector.y_bins,
94 detector.z_bins,
95 ]
96 second_card.sdum = '&'
98 return [first_card, second_card]
101def _parse_cylinder_detector(detector: CylinderDetector, quantity: Quantity, quantity_to_score: str,
102 output_unit_in_fluka_convention: str) -> list[Card]:
103 """Creates USRBIN card for cylinder detector"""
104 first_card = Card(codewd='USRBIN')
105 first_card.what = [
106 '11.0', quantity_to_score, output_unit_in_fluka_convention, detector.r_max, detector.y, detector.z_max
107 ]
108 first_card.sdum = short_name(quantity.name)
110 second_card = Card(codewd='USRBIN')
111 second_card.what = [
112 detector.r_min,
113 detector.x,
114 detector.z_min,
115 detector.r_bins,
116 detector.phi_bins,
117 detector.z_bins,
118 ]
119 second_card.sdum = '&'
121 return [first_card, second_card]
124def handle_auxscore_filter(quantity: Quantity, score_index: int, score_card_type: str = 'USRBIN') -> str:
125 """Creates AUXSCORE card for previously created card"""
126 filter_value: Optional[Union[int, str]] = parse_filter_value(quantity.scoring_filter)
127 if filter_value is None:
128 return ''
129 auxscore = Card(codewd='AUXSCORE')
130 auxscore.what = [
131 score_card_type,
132 filter_value,
133 '',
134 score_index,
135 score_index,
136 1,
137 ]
138 auxscore.sdum = ''
140 return f'{auxscore!s}'
143def parse_filter_value(scoring_filter: Union[CustomFilter, ParticleFilter]) -> Optional[Union[int, str]]:
144 """Parses filter value from filter"""
145 if isinstance(scoring_filter, ParticleFilter):
146 return scoring_filter.particle
147 if isinstance(scoring_filter, CustomFilter):
148 scoring_filter: CustomFilter
149 # According to:
150 # https://flukafiles.web.cern.ch/manual/chapters/description_input/description_options/auxscore.html#auxscore
151 # We are using -(Z*100 + A*100000) for custom filters to define filter
152 # for particles with atomic number equal to Z and mass number equal to A
153 return -(scoring_filter.z * 100 + scoring_filter.a * 100000)
155 return None
158@dataclass
159class ScoringsCard:
160 """Class representing set of scoring cards in Fluka"""
162 data: list[Scoring] = field(default_factory=list)
164 def __str__(self) -> str:
165 # each Scoring card consists of two cards;
166 # the second one is continuation of data included in first
168 # temporary default for no symmetry
169 result: list[str] = []
171 default_output_unit = 21
172 counter = ScoringCardIndexCounter()
173 for scoring in self.data:
174 scoring_cards = handle_scoring_cards(default_output_unit, scoring, counter)
175 if scoring_cards:
176 result.append(scoring_cards)
177 default_output_unit += 1
178 return '\n'.join(result).strip()