├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── CITATION.cff ├── LICENSE ├── README.md ├── conftest.py ├── csg2csg ├── Card.py ├── CellCard.py ├── FLUKACellCard.py ├── FLUKAInput.py ├── FLUKAMaterialCard.py ├── FLUKASurfaceCard.py ├── Input.py ├── MCNPCellCard.py ├── MCNPDataCard.py ├── MCNPFormatter.py ├── MCNPInput.py ├── MCNPMaterialCard.py ├── MCNPParticleNames.py ├── MCNPSurfaceCard.py ├── MaterialCard.py ├── MaterialData.py ├── OpenMCCell.py ├── OpenMCInput.py ├── OpenMCMaterial.py ├── OpenMCSurface.py ├── ParticleNames.py ├── PhitsInput.py ├── SCONECellCard.py ├── SCONEInput.py ├── SCONEMaterialCard.py ├── SCONESurfaceCard.py ├── SCONEUniverseCard.py ├── SerpentCellCard.py ├── SerpentInput.py ├── SerpentMaterialCard.py ├── SerpentSurfaceCard.py ├── SurfaceCard.py ├── UniverseCard.py ├── Vector.py ├── __init__.py ├── __main__.py └── mcnp_tld.mod ├── pyproject.toml ├── test-data └── spheres.i └── tests ├── test_lineendings.py ├── test_materialdata.py ├── test_mcnpcell.py ├── test_mcnpformatter.py ├── test_mcnpinput.py ├── test_mcnpmaterial.py ├── test_mcnpparticle.py ├── test_mcnpsurface.py ├── test_openmcmaterial.py ├── test_particle.py ├── test_serpentmaterial.py ├── test_serpentsurface.py ├── test_surface.py └── test_vector.py /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | 11 | - name: Setup Python 12 | uses: actions/setup-python@v4 13 | with: 14 | python-version: "3.10" 15 | cache: 'pip' 16 | 17 | - name: Install packages 18 | run: | 19 | pip install .[dev] 20 | 21 | - name: Test with pytest 22 | run: | 23 | pytest --cov=csg2csg 24 | 25 | - name: Lint with Ruff 26 | run: | 27 | ruff --format=github --target-version=py37 . 28 | continue-on-error: true 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # Distribution / packaging 7 | .Python 8 | build/ 9 | develop-eggs/ 10 | dist/ 11 | downloads/ 12 | eggs/ 13 | .eggs/ 14 | lib/ 15 | lib64/ 16 | parts/ 17 | sdist/ 18 | var/ 19 | wheels/ 20 | pip-wheel-metadata/ 21 | share/python-wheels/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | MANIFEST 26 | 27 | *~ 28 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Davis" 5 | given-names: "Andrew" 6 | - family-names: "Shimwell" 7 | given-names: "Jonathan" 8 | orcid: "https://orcid.org/0000-0001-6909-0946" 9 | - family-names: "Lilley" 10 | given-names: "Steve" 11 | - family-names: "Shriwise" 12 | given-names: "Patrick" 13 | orcid: "https://orcid.org/0000-0002-3979-7665" 14 | title: "csg2csg: Tools to translate between different CSG geometry types " 15 | version: 0.0.30 16 | date-released: 2021-3-2 17 | url: "https://github.com/makeclean/csg2csg" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | EUROPEAN UNION PUBLIC LICENCE v. 1.2 2 | EUPL © the European Union 2007, 2016 3 | 4 | This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined 5 | below) which is provided under the terms of this Licence. Any use of the Work, 6 | other than as authorised under this Licence is prohibited (to the extent such 7 | use is covered by a right of the copyright holder of the Work). 8 | 9 | The Work is provided under the terms of this Licence when the Licensor (as 10 | defined below) has placed the following notice immediately following the 11 | copyright notice for the Work: 12 | 13 | Licensed under the EUPL 14 | 15 | or has expressed by any other means his willingness to license under the EUPL. 16 | 17 | 1. Definitions 18 | 19 | In this Licence, the following terms have the following meaning: 20 | 21 | - ‘The Licence’: this Licence. 22 | 23 | - ‘The Original Work’: the work or software distributed or communicated by the 24 | Licensor under this Licence, available as Source Code and also as Executable 25 | Code as the case may be. 26 | 27 | - ‘Derivative Works’: the works or software that could be created by the 28 | Licensee, based upon the Original Work or modifications thereof. This Licence 29 | does not define the extent of modification or dependence on the Original Work 30 | required in order to classify a work as a Derivative Work; this extent is 31 | determined by copyright law applicable in the country mentioned in Article 15. 32 | 33 | - ‘The Work’: the Original Work or its Derivative Works. 34 | 35 | - ‘The Source Code’: the human-readable form of the Work which is the most 36 | convenient for people to study and modify. 37 | 38 | - ‘The Executable Code’: any code which has generally been compiled and which is 39 | meant to be interpreted by a computer as a program. 40 | 41 | - ‘The Licensor’: the natural or legal person that distributes or communicates 42 | the Work under the Licence. 43 | 44 | - ‘Contributor(s)’: any natural or legal person who modifies the Work under the 45 | Licence, or otherwise contributes to the creation of a Derivative Work. 46 | 47 | - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of 48 | the Work under the terms of the Licence. 49 | 50 | - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, 51 | renting, distributing, communicating, transmitting, or otherwise making 52 | available, online or offline, copies of the Work or providing access to its 53 | essential functionalities at the disposal of any other natural or legal 54 | person. 55 | 56 | 2. Scope of the rights granted by the Licence 57 | 58 | The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 59 | sublicensable licence to do the following, for the duration of copyright vested 60 | in the Original Work: 61 | 62 | - use the Work in any circumstance and for all usage, 63 | - reproduce the Work, 64 | - modify the Work, and make Derivative Works based upon the Work, 65 | - communicate to the public, including the right to make available or display 66 | the Work or copies thereof to the public and perform publicly, as the case may 67 | be, the Work, 68 | - distribute the Work or copies thereof, 69 | - lend and rent the Work or copies thereof, 70 | - sublicense rights in the Work or copies thereof. 71 | 72 | Those rights can be exercised on any media, supports and formats, whether now 73 | known or later invented, as far as the applicable law permits so. 74 | 75 | In the countries where moral rights apply, the Licensor waives his right to 76 | exercise his moral right to the extent allowed by law in order to make effective 77 | the licence of the economic rights here above listed. 78 | 79 | The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to 80 | any patents held by the Licensor, to the extent necessary to make use of the 81 | rights granted on the Work under this Licence. 82 | 83 | 3. Communication of the Source Code 84 | 85 | The Licensor may provide the Work either in its Source Code form, or as 86 | Executable Code. If the Work is provided as Executable Code, the Licensor 87 | provides in addition a machine-readable copy of the Source Code of the Work 88 | along with each copy of the Work that the Licensor distributes or indicates, in 89 | a notice following the copyright notice attached to the Work, a repository where 90 | the Source Code is easily and freely accessible for as long as the Licensor 91 | continues to distribute or communicate the Work. 92 | 93 | 4. Limitations on copyright 94 | 95 | Nothing in this Licence is intended to deprive the Licensee of the benefits from 96 | any exception or limitation to the exclusive rights of the rights owners in the 97 | Work, of the exhaustion of those rights or of other applicable limitations 98 | thereto. 99 | 100 | 5. Obligations of the Licensee 101 | 102 | The grant of the rights mentioned above is subject to some restrictions and 103 | obligations imposed on the Licensee. Those obligations are the following: 104 | 105 | Attribution right: The Licensee shall keep intact all copyright, patent or 106 | trademarks notices and all notices that refer to the Licence and to the 107 | disclaimer of warranties. The Licensee must include a copy of such notices and a 108 | copy of the Licence with every copy of the Work he/she distributes or 109 | communicates. The Licensee must cause any Derivative Work to carry prominent 110 | notices stating that the Work has been modified and the date of modification. 111 | 112 | Copyleft clause: If the Licensee distributes or communicates copies of the 113 | Original Works or Derivative Works, this Distribution or Communication will be 114 | done under the terms of this Licence or of a later version of this Licence 115 | unless the Original Work is expressly distributed only under this version of the 116 | Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee 117 | (becoming Licensor) cannot offer or impose any additional terms or conditions on 118 | the Work or Derivative Work that alter or restrict the terms of the Licence. 119 | 120 | Compatibility clause: If the Licensee Distributes or Communicates Derivative 121 | Works or copies thereof based upon both the Work and another work licensed under 122 | a Compatible Licence, this Distribution or Communication can be done under the 123 | terms of this Compatible Licence. For the sake of this clause, ‘Compatible 124 | Licence’ refers to the licences listed in the appendix attached to this Licence. 125 | Should the Licensee's obligations under the Compatible Licence conflict with 126 | his/her obligations under this Licence, the obligations of the Compatible 127 | Licence shall prevail. 128 | 129 | Provision of Source Code: When distributing or communicating copies of the Work, 130 | the Licensee will provide a machine-readable copy of the Source Code or indicate 131 | a repository where this Source will be easily and freely available for as long 132 | as the Licensee continues to distribute or communicate the Work. 133 | 134 | Legal Protection: This Licence does not grant permission to use the trade names, 135 | trademarks, service marks, or names of the Licensor, except as required for 136 | reasonable and customary use in describing the origin of the Work and 137 | reproducing the content of the copyright notice. 138 | 139 | 6. Chain of Authorship 140 | 141 | The original Licensor warrants that the copyright in the Original Work granted 142 | hereunder is owned by him/her or licensed to him/her and that he/she has the 143 | power and authority to grant the Licence. 144 | 145 | Each Contributor warrants that the copyright in the modifications he/she brings 146 | to the Work are owned by him/her or licensed to him/her and that he/she has the 147 | power and authority to grant the Licence. 148 | 149 | Each time You accept the Licence, the original Licensor and subsequent 150 | Contributors grant You a licence to their contributions to the Work, under the 151 | terms of this Licence. 152 | 153 | 7. Disclaimer of Warranty 154 | 155 | The Work is a work in progress, which is continuously improved by numerous 156 | Contributors. It is not a finished work and may therefore contain defects or 157 | ‘bugs’ inherent to this type of development. 158 | 159 | For the above reason, the Work is provided under the Licence on an ‘as is’ basis 160 | and without warranties of any kind concerning the Work, including without 161 | limitation merchantability, fitness for a particular purpose, absence of defects 162 | or errors, accuracy, non-infringement of intellectual property rights other than 163 | copyright as stated in Article 6 of this Licence. 164 | 165 | This disclaimer of warranty is an essential part of the Licence and a condition 166 | for the grant of any rights to the Work. 167 | 168 | 8. Disclaimer of Liability 169 | 170 | Except in the cases of wilful misconduct or damages directly caused to natural 171 | persons, the Licensor will in no event be liable for any direct or indirect, 172 | material or moral, damages of any kind, arising out of the Licence or of the use 173 | of the Work, including without limitation, damages for loss of goodwill, work 174 | stoppage, computer failure or malfunction, loss of data or any commercial 175 | damage, even if the Licensor has been advised of the possibility of such damage. 176 | However, the Licensor will be liable under statutory product liability laws as 177 | far such laws apply to the Work. 178 | 179 | 9. Additional agreements 180 | 181 | While distributing the Work, You may choose to conclude an additional agreement, 182 | defining obligations or services consistent with this Licence. However, if 183 | accepting obligations, You may act only on your own behalf and on your sole 184 | responsibility, not on behalf of the original Licensor or any other Contributor, 185 | and only if You agree to indemnify, defend, and hold each Contributor harmless 186 | for any liability incurred by, or claims asserted against such Contributor by 187 | the fact You have accepted any warranty or additional liability. 188 | 189 | 10. Acceptance of the Licence 190 | 191 | The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ 192 | placed under the bottom of a window displaying the text of this Licence or by 193 | affirming consent in any other similar way, in accordance with the rules of 194 | applicable law. Clicking on that icon indicates your clear and irrevocable 195 | acceptance of this Licence and all of its terms and conditions. 196 | 197 | Similarly, you irrevocably accept this Licence and all of its terms and 198 | conditions by exercising any rights granted to You by Article 2 of this Licence, 199 | such as the use of the Work, the creation by You of a Derivative Work or the 200 | Distribution or Communication by You of the Work or copies thereof. 201 | 202 | 11. Information to the public 203 | 204 | In case of any Distribution or Communication of the Work by means of electronic 205 | communication by You (for example, by offering to download the Work from a 206 | remote location) the distribution channel or media (for example, a website) must 207 | at least provide to the public the information requested by the applicable law 208 | regarding the Licensor, the Licence and the way it may be accessible, concluded, 209 | stored and reproduced by the Licensee. 210 | 211 | 12. Termination of the Licence 212 | 213 | The Licence and the rights granted hereunder will terminate automatically upon 214 | any breach by the Licensee of the terms of the Licence. 215 | 216 | Such a termination will not terminate the licences of any person who has 217 | received the Work from the Licensee under the Licence, provided such persons 218 | remain in full compliance with the Licence. 219 | 220 | 13. Miscellaneous 221 | 222 | Without prejudice of Article 9 above, the Licence represents the complete 223 | agreement between the Parties as to the Work. 224 | 225 | If any provision of the Licence is invalid or unenforceable under applicable 226 | law, this will not affect the validity or enforceability of the Licence as a 227 | whole. Such provision will be construed or reformed so as necessary to make it 228 | valid and enforceable. 229 | 230 | The European Commission may publish other linguistic versions or new versions of 231 | this Licence or updated versions of the Appendix, so far this is required and 232 | reasonable, without reducing the scope of the rights granted by the Licence. New 233 | versions of the Licence will be published with a unique version number. 234 | 235 | All linguistic versions of this Licence, approved by the European Commission, 236 | have identical value. Parties can take advantage of the linguistic version of 237 | their choice. 238 | 239 | 14. Jurisdiction 240 | 241 | Without prejudice to specific agreement between parties, 242 | 243 | - any litigation resulting from the interpretation of this License, arising 244 | between the European Union institutions, bodies, offices or agencies, as a 245 | Licensor, and any Licensee, will be subject to the jurisdiction of the Court 246 | of Justice of the European Union, as laid down in article 272 of the Treaty on 247 | the Functioning of the European Union, 248 | 249 | - any litigation arising between other parties and resulting from the 250 | interpretation of this License, will be subject to the exclusive jurisdiction 251 | of the competent court where the Licensor resides or conducts its primary 252 | business. 253 | 254 | 15. Applicable Law 255 | 256 | Without prejudice to specific agreement between parties, 257 | 258 | - this Licence shall be governed by the law of the European Union Member State 259 | where the Licensor has his seat, resides or has his registered office, 260 | 261 | - this licence shall be governed by Belgian law if the Licensor has no seat, 262 | residence or registered office inside a European Union Member State. 263 | 264 | Appendix 265 | 266 | ‘Compatible Licences’ according to Article 5 EUPL are: 267 | 268 | - GNU General Public License (GPL) v. 2, v. 3 269 | - GNU Affero General Public License (AGPL) v. 3 270 | - Open Software License (OSL) v. 2.1, v. 3.0 271 | - Eclipse Public License (EPL) v. 1.0 272 | - CeCILL v. 2.0, v. 2.1 273 | - Mozilla Public Licence (MPL) v. 2 274 | - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 275 | - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for 276 | works other than software 277 | - European Union Public Licence (EUPL) v. 1.1, v. 1.2 278 | - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong 279 | Reciprocity (LiLiQ-R+). 280 | 281 | The European Commission may update this Appendix to later versions of the above 282 | licences without producing a new version of the EUPL, as long as they provide 283 | the rights granted in Article 2 of this Licence and protect the covered Source 284 | Code from exclusive appropriation. 285 | 286 | All other changes or additions to this Appendix require the production of a new 287 | EUPL version. 288 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI test results](https://github.com/makeclean/csg2csg/actions/workflows/ci.yaml/badge.svg)](https://github.com/makeclean/csg2csg/actions/workflows/ci.yaml) 2 | 3 | # csg2csg 4 | A tool to translate common Monte Carlo geometry formats between each other. 5 | 6 | ## How to use 7 | Right now the code is petty rough. 8 | 9 | Install the latest version released on PyPi using pip 10 | ```bash 11 | pip3 install csg2csg --user 12 | ``` 13 | 14 | Install the latest version on the repository using pip 15 | ```bash 16 | pip install git+https://github.com/makeclean/csg2csg.git --user 17 | ``` 18 | 19 | Run from the terminal with ``csg2csg -i filename -f format`` where filename is the input filename and format is the file format of the input file. Options for the file format include several neutronics codes [mcnp, serpent, openmc, phits, fluka] each is at a different level of completeness. 20 | 21 | The default is to attempt to translate the input file into all neutronics codes but users can specify which codes with the ``-o`` flag. 22 | 23 | To see all the run options type ``csg2csg -h`` in the terminal 24 | 25 | ## Caveats 26 | Several! Right now only MCNP can be read, and then written to MCNP, Serpent and OpenMC. 27 | When the file can be read only a subset of MCNP surfaces can be read 28 | 29 | MCNP Surfaces Supported 30 | - P,PX,PY,PZ 31 | - S SO SX SY SZ 32 | - CX CY CZ C/X C/Y C/Z 33 | - SQ 34 | - GQ 35 | - KX, KY, KZ 36 | - TX TY TZ 37 | - Macrobodies - RPP, SPH and RCC 38 | - X, Y, Z - one and two coefficent only 39 | 40 | 41 | MCNP Surfaces Not Yet Supported 42 | - X, Y, Z - three coefficient 43 | - Macrobodies - BOX, RHP, HEX, REC, TRC, ELL, WEB, ARB 44 | 45 | Transforms 46 | - Are read and interpretted, but nothing is done with them, in the future codes that support cell transformations will use it, but right now MCNP is the only code that does surface transformations 47 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makeclean/csg2csg/88e79f9f098494c45e9f2f4ec8746627a5e2a939/conftest.py -------------------------------------------------------------------------------- /csg2csg/Card.py: -------------------------------------------------------------------------------- 1 | class Card: 2 | """Generic Card class from which all other card classes inherit""" 3 | 4 | text_string = "" 5 | 6 | def __init__(self, card_string): 7 | self.text_string = card_string 8 | 9 | def __str__(self): 10 | return text_string 11 | -------------------------------------------------------------------------------- /csg2csg/CellCard.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | from csg2csg.Card import Card 4 | from enum import Enum 5 | 6 | 7 | class CellCard(Card): 8 | """Class for the storage of the Generic CellCard type 9 | methods for the generation of CellCards should be placed 10 | here. Classes instanciating CellCard objects should be 11 | implemented in its own CodeCellCard.py file 12 | """ 13 | 14 | class OperationType(Enum): 15 | def __str__(self): 16 | return str(self.value) 17 | 18 | NOT = 2 19 | UNION = 1 20 | AND = 0 21 | 22 | # constructor for the cellcard class 23 | def __init__(self, card_string): 24 | 25 | self.cell_comment = "" 26 | self.cell_id = 0 27 | self.cell_density = 0 28 | self.cell_material_number = 0 29 | self.cell_importance = 1 # note any importance - we assume everything else is 0 30 | self.cell_text_description = "" 31 | self.cell_interpreted = "" 32 | self.cell_fill = 0 33 | self.cell_universe = 0 34 | self.cell_universe_offset = 0 35 | self.cell_universe_rotation = 0 36 | self.cell_universe_transformation_id = ( 37 | "0" # if there is a cell_universe tr number it should be purged 38 | ) 39 | # and converted into an offset and rotation 40 | self.cell_surface_list = set() # list of surfaces used in the cell definition 41 | 42 | Card.__init__(self, card_string) 43 | 44 | # print method 45 | def __str__(self): 46 | string = "Cell Card: \n" 47 | string += "Cell ID " + str(self.cell_id) + "\n" 48 | string += "Material Number " + str(self.cell_material_number) + "\n" 49 | string += "Material Density " + str(self.cell_density) + "\n" 50 | string += "Importance " + str(self.cell_importance) + "\n" 51 | string += "Comment " + str(self.cell_comment) + "\n" 52 | string += "Text Description " + str(self.cell_text_description) + "\n" 53 | string += "Cell Description " + str(self.cell_interpreted) + "\n" 54 | string += "Surfs in Cell " + str(self.cell_surface_list) + "\n" 55 | return string 56 | 57 | """ Look for surface new in the list 58 | and replace with surface reference 59 | """ 60 | 61 | def replace_surface(self, reference_surface, new, reverse): 62 | # self.cell_surface_list.remove(reference_surface) 63 | # self.cell_surface_list.add(new) 64 | 65 | # loop over the cell looking for surfaces 66 | for idx, item in enumerate(self.cell_interpreted): 67 | # if the surface has -ve or +ve sense and doesnt 68 | # need reversing just insert it 69 | if not isinstance(item, self.OperationType) and item not in {"(", ")"}: 70 | surf = int(item) 71 | 72 | if abs(surf) == new and surf == new: 73 | if not reverse: 74 | self.cell_interpreted[idx] = str(int(reference_surface)) 75 | else: 76 | self.cell_interpreted[idx] = str(-1 * int(reference_surface)) 77 | 78 | elif abs(surf) == new and surf != new: 79 | if not reverse: 80 | self.cell_interpreted[idx] = str(-1 * int(reference_surface)) 81 | else: 82 | self.cell_interpreted[idx] = str(int(reference_surface)) 83 | 84 | return 85 | -------------------------------------------------------------------------------- /csg2csg/FLUKACellCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.CellCard import CellCard 4 | from enum import Enum 5 | import re 6 | 7 | # turn the generic operation type into a serpent relevant text string 8 | def fluka_op_from_generic(Operation): 9 | # if we are not of type operator - we are string do nowt 10 | if not isinstance(Operation, CellCard.OperationType): 11 | if Operation == "(": 12 | return " +" + Operation + " " 13 | elif Operation == ")": 14 | return " " + Operation + " " 15 | elif int(Operation) < 0: 16 | return " +S" + str(abs(int(Operation))) 17 | elif int(Operation) > 0: 18 | return " -S" + str(int(Operation)) 19 | else: 20 | return Operation 21 | else: 22 | # otherwise we need to do something 23 | if Operation == CellCard.OperationType["NOT"]: 24 | string = " -" 25 | elif Operation == CellCard.OperationType["AND"]: 26 | string = " " 27 | elif Operation == CellCard.OperationType["UNION"]: 28 | string = " | " 29 | else: 30 | string = "unknown operation" 31 | # return the operation 32 | return string 33 | 34 | 35 | # write the cell card for a fluka cell given a generic cell card 36 | def write_fluka_cell(filestream, CellCard): 37 | 38 | string = " C" + str(CellCard.cell_id) + " 5 " # number of adjacent cells 39 | 40 | string += "( " 41 | 42 | # build the cell description 43 | for item in CellCard.cell_interpreted: 44 | string += fluka_op_from_generic(item) 45 | 46 | string += " ) " 47 | string += "\n" 48 | 49 | # removes any multiple spaces 50 | string = re.sub(" ", " ", string) 51 | string = re.sub("\- \+", " -", string) 52 | 53 | filestream.write(string) 54 | 55 | 56 | class FLUKACellCard(CellCard): 57 | def __init__(self, card_string): 58 | CellCard.__init__(self, card_string) 59 | -------------------------------------------------------------------------------- /csg2csg/FLUKAInput.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | import warnings 4 | 5 | from csg2csg.Input import InputDeck 6 | from csg2csg.FLUKASurfaceCard import FLUKASurfaceCard, write_fluka_surface 7 | from csg2csg.FLUKACellCard import FLUKACellCard, write_fluka_cell 8 | from csg2csg.FLUKAMaterialCard import ( 9 | FLUKAMaterialCard, 10 | write_fluka_material, 11 | write_fluka_material_element, 12 | write_fluka_compound, 13 | ) 14 | 15 | fluka_special_mats = [ 16 | "HYDROGEN", 17 | "HELIUM", 18 | "BERYLLIU", 19 | "CARBON", 20 | "NITROGEN", 21 | "OXYGEN", 22 | "MAGNESIU", 23 | "ALUMINUM", 24 | "IRON", 25 | "COPPER", 26 | "SILVER", 27 | "SILICON", 28 | "GOLD", 29 | "MERCURY", 30 | "LEAD", 31 | "TANTALUM", 32 | "SODIUM", 33 | "ARGON", 34 | "CALCIUM", 35 | "TIN", 36 | "TUNGSTEN", 37 | "TITANIUM", 38 | "NICKEL", 39 | ] 40 | 41 | zz_to_remove = [ 42 | 34000, 43 | 37000, 44 | 39000, 45 | 44000, 46 | 45000, 47 | 59000, 48 | 61000, 49 | 63000, 50 | 66000, 51 | 67000, 52 | 68000, 53 | 69000, 54 | 70000, 55 | 76000, 56 | 81000, 57 | 84000, 58 | 85000, 59 | 86000, 60 | 87000, 61 | 88000, 62 | 89000, 63 | 91000, 64 | 93000, 65 | ] 66 | 67 | zz_to_collapse = [ 68 | 4000, 69 | 6000, 70 | 7000, 71 | 8000, 72 | 9000, 73 | 10000, 74 | 11000, 75 | 12000, 76 | 13000, 77 | 14000, 78 | 15000, 79 | 16000, 80 | 17000, 81 | 19000, 82 | 20000, 83 | 21000, 84 | 22000, 85 | 23000, 86 | 24000, 87 | 25000, 88 | 26000, 89 | 27000, 90 | 28000, 91 | 29000, 92 | 30000, 93 | 31000, 94 | 32000, 95 | 33000, 96 | 35000, 97 | 36000, 98 | 52000, 99 | 56000, 100 | 57000, 101 | 58000, 102 | 60000, 103 | 62000, 104 | 64000, 105 | 65000, 106 | 71000, 107 | 72000, 108 | 73000, 109 | 74000, 110 | 75000, 111 | 77000, 112 | 78000, 113 | 79000, 114 | 80000, 115 | 82000, 116 | 83000, 117 | 94000, 118 | 95000, 119 | ] 120 | 121 | zz_to_fluka = { 122 | 1000: "HYDROGEN", 123 | 1001: "HYDROG-1", 124 | 1002: "DEUTERIU", 125 | 1003: "TRITIUM", 126 | 2000: "HELIUM", 127 | 2003: "HELIUM-3", 128 | 2004: "HELIUM-4", 129 | 3000: "LITHIUM", 130 | 3006: "LITHIU-6", 131 | 3007: "LITHIU-7", 132 | 4000: "BERYLLIU", 133 | 5000: "BORON", 134 | 5010: "BORON-10", 135 | 5011: "BORON-11", 136 | 6000: "CARBON", 137 | 7000: "NITROGEN", 138 | 8000: "OXYGEN", 139 | 9000: "FLUORINE", 140 | 10000: "NEON", 141 | 11000: "SODIUM", 142 | 12000: "MAGNESIU", 143 | 13000: "ALUMINUM", 144 | 14000: "SILICON", 145 | 15000: "PHOSPHO", 146 | 16000: "SULFUR", 147 | 17000: "CHLORINE", 148 | 18000: "ARGON", 149 | 18040: "ARGON-40", 150 | 19000: "POTASSIU", 151 | 20000: "CALCIUM", 152 | 21000: "SCANDIUM", 153 | 22000: "TITANIUM", 154 | 23000: "VANADIUM", 155 | 24000: "CHROMIUM", 156 | 25000: "MANGANES", 157 | 26000: "IRON", 158 | 27000: "COBALT", 159 | 28000: "NICKEL", 160 | 29000: "COPPER", 161 | 30000: "ZINC", 162 | 31000: "GALLIUM", 163 | 32000: "GERMANIU", 164 | 33000: "ARSENIC", 165 | 35000: "BROMINE", 166 | 36000: "KRYPTON", 167 | 38000: "STRONTIU", 168 | 38090: "90-SR", 169 | 40000: "ZIRCONIU", 170 | 41000: "NIOBIUM", 171 | 42000: "MOLYBDEN", 172 | 43000: "99-TC", 173 | 46000: "PALLADIU", 174 | 47000: "SILVER", 175 | 48000: "CADMIUM", 176 | 49000: "INDIUM", 177 | 50000: "TIN", 178 | 51000: "ANTIMONY", 179 | 53000: "IODINE", 180 | 53129: "129-I", 181 | 54000: "XENON", 182 | 54124: "124-XE", 183 | 54126: "126-XE", 184 | 54128: "128-XE", 185 | 54129: "129-XE", 186 | 54130: "130-XE", 187 | 54131: "131-XE", 188 | 54132: "132-XE", 189 | 54134: "134-XE", 190 | 54135: "135-XE", 191 | 54136: "136-XE", 192 | 55000: "CESIUM", 193 | 55135: "135-CS", 194 | 55137: "137-CS", 195 | 56000: "BARIUM", 196 | 57000: "LANTHANU", 197 | 58000: "CERIUM", 198 | 60000: "NEODYMIU", 199 | 62000: "SAMARIUM", 200 | 64000: "GADOLINI", 201 | 65000: "TERBIUM", 202 | 71000: "LUTETIUM", 203 | 72000: "HAFNIUM", 204 | 73000: "TANTALUM", 205 | 74000: "TUNGSTEN", 206 | 75000: "RHENIUM", 207 | 77000: "IRIDIUM", 208 | 78000: "PLATINUM", 209 | 79000: "GOLD", 210 | 80000: "MERCURY", 211 | 82000: "LEAD", 212 | 83000: "BISMUTH", 213 | 90230: "230-TH", 214 | 90232: "232-TH", 215 | 92233: "233-U", 216 | 92234: "234-U", 217 | 92235: "235-U", 218 | 92238: "238-U", 219 | 94239: "239-PU", 220 | 95241: "241-AM", 221 | 98000: "CALIFORN", 222 | } 223 | 224 | 225 | class FLUKAInput(InputDeck): 226 | """FlukaInput class - does the actual processing""" 227 | 228 | # constructor 229 | def __init__(self, filename=""): 230 | InputDeck.__init__(self, filename) 231 | 232 | # write the fluka input deck ruler 233 | def __write_ruler(self, filestream): 234 | filestream.write( 235 | "*...+....1....+....2....+....3....+....4" 236 | + "....+....5....+....6....+....7....+....8\n" 237 | ) 238 | 239 | # Write the Serpent Cell definitions 240 | def __write_fluka_cells(self, filestream): 241 | filestream.write("* --- cell definitions --- *\n") 242 | for cell in self.cell_list: 243 | write_fluka_cell(filestream, cell) 244 | filestream.write("END\n") 245 | filestream.write("GEOEND\n") 246 | return 247 | 248 | # write the serpent surface definitions 249 | def __write_fluka_surfaces(self, filestream): 250 | filestream.write("* --- surface definitions --- *\n") 251 | self.__write_ruler(filestream) 252 | filestream.write( 253 | "GEOBEGIN COMBNAME\n" 254 | ) 255 | filestream.write(" 0 0\n") 256 | for surface in self.surface_list: 257 | write_fluka_surface(filestream, surface) 258 | filestream.write("END\n") 259 | return 260 | 261 | def __write_fluka_importances(self, filename): 262 | max_importance = 1 263 | min_importance = 1e9 264 | 265 | for cell in self.cell_list: 266 | if cell.cell_importance > max_importance: 267 | max_importance = cell.cell_importance 268 | if cell.cell_importance > 0 and cell.cell_importance < min_importance: 269 | min_importance = cell.cell_importance 270 | 271 | max_importance /= (min_importance) * ( 272 | max_importance / 1e4 273 | ) # to scale for fluka range 274 | 275 | if max_importance / min_importance > 1e9: 276 | warnings.warn( 277 | "In Fluka found an importance greater than 1e9, truncated", Warning 278 | ) 279 | 280 | for cell in self.cell_list: 281 | string = "{:<10}".format("BIASING") 282 | string += "{:>10}".format("3.0") 283 | string += "{:>10}".format("1.0") 284 | if cell.cell_importance == 0.0: 285 | importance = 1.0e-4 286 | else: 287 | importance = cell.cell_importance / max_importance 288 | 289 | if importance > 100000.0: 290 | importance = 100000.0 291 | string += "{:>10.4e}".format(importance) 292 | string += "{:>10}".format("C" + str(cell.cell_id)) 293 | string += "{:>10}".format("") 294 | string += "{:>10}".format("") 295 | string += "{:>10}".format("PRINT") 296 | string += "\n" 297 | 298 | filename.write(string) 299 | 300 | return 301 | 302 | # write the material assignments 303 | def __write_fluka_assignmats(self, filestream): 304 | self.__write_ruler(filestream) 305 | for cell in self.cell_list: 306 | # needs to be 10 chars 307 | region = "C" + str(cell.cell_id) 308 | if cell.cell_material_number != 0: 309 | mat = "M" + str(cell.cell_material_number) 310 | elif cell.cell_material_number == 0 and cell.cell_importance == 0: 311 | mat = "BLCKHOLE" 312 | else: 313 | mat = "VACUUM" 314 | 315 | string = "{:<10}".format("ASSIGNMA") 316 | string = string + "{:>10}".format(mat) 317 | string = string + "{:>10}".format(region) 318 | string = string + "\n" 319 | filestream.write(string) 320 | return 321 | 322 | # write the material compositions 323 | def __write_fluka_materials(self, filestream): 324 | filestream.write("* --- material definitions --- *\n") 325 | self.__write_ruler(filestream) 326 | 327 | # first loop through the materials and build the unqiue list 328 | # of fluka based materials 329 | nuclide_set = set() 330 | for material in self.material_list.keys(): 331 | for nuclide in self.material_list[material].composition_dictionary: 332 | nuclide_set.add(nuclide) 333 | 334 | collapsed_map = {} 335 | # nuclide set is the map of zaid to Fluka name 336 | for nuc in nuclide_set: 337 | if int(nuc) in zz_to_fluka.keys(): 338 | collapsed_map[nuc] = zz_to_fluka[int(nuc)] 339 | else: 340 | if int(float(nuc) / 1000) * 1000 not in zz_to_remove: 341 | collapsed_map[nuc] = zz_to_fluka[int(float(nuc) / 1000) * 1000] 342 | 343 | written = set() 344 | # keep track of those already written so we dont call them out multiple times 345 | for nuc in collapsed_map.keys(): 346 | if ( 347 | collapsed_map[nuc] not in fluka_special_mats 348 | and collapsed_map[nuc] not in written 349 | ): 350 | write_fluka_material_element(filestream, nuc, collapsed_map[nuc]) 351 | written.add(collapsed_map[nuc]) 352 | 353 | # operate on the list of materials present and update the composition maps 354 | # to have fluka names instead of zaid 355 | for material in self.material_list.keys(): 356 | mat_dict = self.material_list[material].composition_dictionary 357 | name_dict = {} 358 | 359 | # loop over the collpased map 360 | for nuc in collapsed_map.keys(): 361 | if nuc in mat_dict.keys(): 362 | # get the fluka name 363 | fluka_name = collapsed_map[nuc] 364 | if fluka_name in name_dict.keys(): 365 | name_dict[fluka_name] += mat_dict[nuc] 366 | else: 367 | name_dict[fluka_name] = mat_dict[nuc] 368 | # if name doesnt exist add it, otherwise 369 | # add to it 370 | self.material_list[material].composition_dictionary = name_dict 371 | 372 | # collapsed map now has the zaid - fluka name map 373 | # for each material not in the special list - call it out 374 | for material in self.material_list: 375 | write_fluka_material(filestream, self.material_list[material]) 376 | write_fluka_compound(filestream, self.material_list[material]) 377 | # write_fluka_lowmat(filestream, self.material_list[material]) 378 | return 379 | 380 | # main write serpent method, depending upon where the geometry 381 | # came from 382 | def write_fluka(self, filename, flat=True): 383 | f = open(filename, "w") 384 | self.__write_ruler(f) 385 | f.write("TITLE\n input deck automatically created by csg2csg\n") 386 | self.__write_ruler(f) 387 | self.__write_fluka_surfaces(f) 388 | self.__write_ruler(f) 389 | self.__write_fluka_cells(f) 390 | self.__write_ruler(f) 391 | self.__write_fluka_importances(f) 392 | self.__write_ruler(f) 393 | self.__write_fluka_assignmats(f) 394 | self.__write_fluka_materials(f) 395 | self.__write_ruler(f) 396 | f.write("STOP\n") 397 | f.close() 398 | -------------------------------------------------------------------------------- /csg2csg/FLUKAMaterialCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.MaterialCard import MaterialCard 4 | 5 | 6 | """ A function which writes the fluka material card for an element 7 | zz is the zaid of the element and name is the fluka name 8 | 9 | Input: filestream - an openfilestream to write to 10 | zz - the zaid of the the element 11 | name - the fluka name of the element 12 | """ 13 | 14 | 15 | def write_fluka_material_element(filestream, zz, name, density="1.0"): 16 | string = "{:<10}".format("MATERIAL") 17 | atomic_number = "{:>10}".format(float(int(int(zz) / 1000))) 18 | atomic_mass = "{:>10}".format("") 19 | density = "{:>10}".format(density) 20 | number = "{:>10}".format("") 21 | alternate = "{:>10}".format("") 22 | mass_num = "{:>10}".format("") 23 | name = "{:>10}".format(name) 24 | string += atomic_number 25 | string += atomic_mass 26 | string += density 27 | string += number 28 | string += alternate 29 | string += mass_num 30 | string += name 31 | string += "\n" 32 | filestream.write(string) 33 | return 34 | 35 | 36 | def write_fluka_material(filestream, material): 37 | string = "{:<10}".format("MATERIAL") 38 | atomic_number = "{:>10}".format("") 39 | atomic_mass = "{:>10}".format("") 40 | density = "{:>10}".format(abs(material.density)) 41 | number = "{:>10}".format("") 42 | alternate = "{:>10}".format("") 43 | mass_num = "{:>10}".format("") 44 | name = "{:>10}".format("M" + str(material.material_number)) 45 | string += atomic_number 46 | string += atomic_mass 47 | string += density 48 | string += number 49 | string += alternate 50 | string += mass_num 51 | string += name 52 | string += "\n" 53 | filestream.write(string) 54 | return 55 | 56 | 57 | def write_fluka_compound(filestream, material): 58 | comp_dict = material.composition_dictionary 59 | card_string = "{:<10}".format("COMPOUND") 60 | mat_name = "{:>10}".format("M" + material.material_number) 61 | 62 | comp_string = "" 63 | for mat, frac in comp_dict.items(): 64 | fraction = "{:> 9.3e}".format(float(frac)) 65 | elemname = "{:>10}".format(mat) 66 | comp_string += fraction 67 | comp_string += elemname 68 | 69 | # work backwards popping off 3 until there none left 70 | while len(comp_string) > 0: 71 | part = "" 72 | if len(comp_string) / 20 >= 3: 73 | part = comp_string[-60:] 74 | comp_string = comp_string[:-60] 75 | elif int(len(comp_string) / 20) == 2: 76 | part = comp_string[-40:] 77 | part += "{:>20}".format("") 78 | comp_string = comp_string[:-40] 79 | elif int(len(comp_string) / 20) == 1: 80 | part = comp_string[-20:] 81 | part += "{:>40}".format("") 82 | comp_string = comp_string[:-20] 83 | 84 | string = card_string 85 | string += part 86 | string += mat_name 87 | string += "\n" 88 | filestream.write(string) 89 | return 90 | 91 | 92 | """ Class to handle FLUKAMaterialCard tranlation 93 | """ 94 | 95 | 96 | class FLUKAMaterialCard(MaterialCard): 97 | def __init__(self, card_string): 98 | MaterialCard.__init__(self, card_string) 99 | -------------------------------------------------------------------------------- /csg2csg/FLUKASurfaceCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.SurfaceCard import SurfaceCard 4 | from math import sqrt 5 | 6 | import warnings 7 | 8 | # write the general form of a plane 9 | def fluka_plane_string(SurfaceCard): 10 | a = SurfaceCard.surface_coefficients[0] 11 | b = SurfaceCard.surface_coefficients[1] 12 | c = SurfaceCard.surface_coefficients[2] 13 | d = SurfaceCard.surface_coefficients[3] 14 | mag = a**2 + b**2 + c**2 15 | x_p = a * (a * 0.0 + b * 0.0 + c * 0.0 + d) / mag 16 | y_p = b * (a * 0.0 + b * 0.0 + c * 0.0 + d) / mag 17 | z_p = c * (a * 0.0 + b * 0.0 + c * 0.0 + d) / mag 18 | 19 | string = "PLA S" + str(SurfaceCard.surface_id) + " " 20 | string += str(a) + " " 21 | string += str(b) + " " 22 | string += str(c) + " " 23 | string += str(x_p) + " " 24 | string += str(y_p) + " " 25 | string += str(z_p) + " " 26 | string += "\n" 27 | 28 | return string 29 | 30 | 31 | # write the specific x form of the plane 32 | def fluka_plane_x_string(SurfaceCard): 33 | string = ( 34 | "YZP S" 35 | + str(SurfaceCard.surface_id) 36 | + " " 37 | + str(SurfaceCard.surface_coefficients[3]) 38 | + "\n" 39 | ) 40 | return string 41 | 42 | 43 | # write the specific y form of the plane 44 | def fluka_plane_y_string(SurfaceCard): 45 | string = ( 46 | "XZP S" 47 | + str(SurfaceCard.surface_id) 48 | + " " 49 | + str(SurfaceCard.surface_coefficients[3]) 50 | + "\n" 51 | ) 52 | return string 53 | 54 | 55 | # write the specific z form of the plane 56 | def fluka_plane_z_string(SurfaceCard): 57 | string = ( 58 | "XYP S" 59 | + str(SurfaceCard.surface_id) 60 | + " " 61 | + str(SurfaceCard.surface_coefficients[3]) 62 | + "\n" 63 | ) 64 | return string 65 | 66 | 67 | # write a cylinder_x 68 | def fluka_cylinder_x(SurfaceCard): 69 | string = ( 70 | "XCC S" 71 | + str(SurfaceCard.surface_id) 72 | + " " 73 | + str(SurfaceCard.surface_coefficients[0]) 74 | + " " 75 | ) 76 | string += str(SurfaceCard.surface_coefficients[1]) + " " 77 | string += str(SurfaceCard.surface_coefficients[2]) + " " 78 | string += "\n" 79 | return string 80 | 81 | 82 | # write a cylinder_y 83 | def fluka_cylinder_y(SurfaceCard): 84 | string = ( 85 | "YCC S" 86 | + str(SurfaceCard.surface_id) 87 | + " " 88 | + str(SurfaceCard.surface_coefficients[1]) 89 | + " " 90 | ) 91 | string += str(SurfaceCard.surface_coefficients[0]) + " " 92 | string += str(SurfaceCard.surface_coefficients[2]) + " " 93 | string += "\n" 94 | return string 95 | 96 | 97 | # write a cylinder_z 98 | def fluka_cylinder_z(SurfaceCard): 99 | string = ( 100 | "ZCC S" 101 | + str(SurfaceCard.surface_id) 102 | + " " 103 | + str(SurfaceCard.surface_coefficients[0]) 104 | + " " 105 | ) 106 | string += str(SurfaceCard.surface_coefficients[1]) + " " 107 | string += str(SurfaceCard.surface_coefficients[2]) + " " 108 | string += "\n" 109 | return string 110 | 111 | 112 | # write a sphere 113 | def fluka_sphere(SurfaceCard): 114 | string = ( 115 | "SPH S" 116 | + str(SurfaceCard.surface_id) 117 | + " " 118 | + str(SurfaceCard.surface_coefficients[0]) 119 | + " " 120 | ) 121 | string += str(SurfaceCard.surface_coefficients[1]) + " " 122 | string += str(SurfaceCard.surface_coefficients[2]) + " " 123 | string += str(SurfaceCard.surface_coefficients[3]) 124 | string += "\n" 125 | return string 126 | 127 | 128 | # write a general quadratic 129 | def fluka_gq(SurfaceCard): 130 | string = "QUA S" + str(SurfaceCard.surface_id) 131 | ax2 = SurfaceCard.surface_coefficients[0] 132 | by2 = SurfaceCard.surface_coefficients[1] 133 | cz2 = SurfaceCard.surface_coefficients[2] 134 | 135 | dxy = SurfaceCard.surface_coefficients[3] 136 | eyz = SurfaceCard.surface_coefficients[4] 137 | fzx = SurfaceCard.surface_coefficients[5] 138 | 139 | gx = SurfaceCard.surface_coefficients[6] 140 | hy = SurfaceCard.surface_coefficients[7] 141 | jz = SurfaceCard.surface_coefficients[8] 142 | 143 | k = SurfaceCard.surface_coefficients[9] 144 | 145 | string += " " + str(ax2) + " " 146 | string += " " + str(by2) + " " 147 | string += " " + str(cz2) + " " 148 | 149 | # fluka wants dxy fzx eyz 150 | 151 | string += " " + str(dxy) + " " 152 | string += " " + str(fzx) + " " 153 | string += " " + str(eyz) + " " 154 | 155 | string += " " + str(gx) + " " 156 | string += " " + str(hy) + " " 157 | string += " " + str(jz) + " " 158 | 159 | string += " " + str(k) + " " 160 | 161 | string += "\n" 162 | return string 163 | 164 | 165 | # its not clear how we deal with +-1 cones for fluka} 166 | # write a cone along x 167 | def fluka_cone_x(SurfaceCard): 168 | 169 | # if the cone direction is specified 170 | if SurfaceCard.surface_coefficients[4] != 0.0: 171 | # cone points down from xyz 172 | h = 0 173 | x = 0 174 | if SurfaceCard.surface_coefficients[4] == -1: 175 | x = SurfaceCard.b_box[0] 176 | h = abs(SurfaceCard.b_box[0]) + SurfaceCard.surface_coefficients[0] 177 | # cone points up from xyz 178 | elif SurfaceCard.surface_coefficients[4] == 1: 179 | x = SurfaceCard.b_box[1] 180 | h = -1.0 * (SurfaceCard.b_box[1] - SurfaceCard.surface_coefficients[0]) 181 | 182 | y = SurfaceCard.surface_coefficients[1] 183 | z = SurfaceCard.surface_coefficients[2] 184 | r = abs(h) * sqrt(SurfaceCard.surface_coefficients[3]) 185 | 186 | # maybe change to use proper formatting statements 187 | string = ( 188 | "TRC S" 189 | + str(SurfaceCard.surface_id) 190 | + " " 191 | + str(x) 192 | + " " 193 | + str(y) 194 | + " " 195 | + str(z) 196 | + " " 197 | + str(h) 198 | + " 0. 0. " 199 | + str(r) 200 | + " 0.0\n" 201 | ) 202 | else: 203 | coefficients = [0.0] * 10 204 | t = SurfaceCard.surface_coefficients[3] 205 | x_bar = SurfaceCard.surface_coefficients[0] 206 | y_bar = SurfaceCard.surface_coefficients[1] 207 | z_bar = SurfaceCard.surface_coefficients[2] 208 | coefficients[0] = t 209 | coefficients[1] = 1.0 210 | coefficients[2] = 1.0 211 | coefficients[3] = 0.0 212 | coefficients[4] = 0.0 213 | coefficients[5] = 0.0 214 | coefficients[6] = -2.0 * t * x_bar 215 | coefficients[7] = -2.0 * y_bar 216 | coefficients[8] = -2.0 * z_bar 217 | coefficients[9] = (t * x_bar**2) + y_bar**2 + z_bar**2 218 | SurfaceCard.surface_coefficients = coefficients 219 | string = fluka_gq(SurfaceCard) 220 | 221 | return string 222 | 223 | 224 | # fluka a cone along y 225 | def fluka_cone_y(SurfaceCard): 226 | string = "" 227 | # if the cone direction is specified 228 | if SurfaceCard.surface_coefficients[4] != 0.0: 229 | # cone points down from xyz 230 | if SurfaceCard.surface_coefficients[4] == -1: 231 | y = SurfaceCard.b_box[2] 232 | h = abs(SurfaceCard.b_box[2]) + SurfaceCard.surface_coefficients[1] 233 | # cone point up from xyz 234 | elif SurfaceCard.surface_coefficients[4] == 1: 235 | y = SurfaceCard.b_box[3] 236 | h = -1.0 * (SurfaceCard.b_box[3] - SurfaceCard.surface_coefficients[1]) 237 | 238 | x = SurfaceCard.surface_coefficients[0] 239 | z = SurfaceCard.surface_coefficients[2] 240 | r = abs(h) * sqrt(SurfaceCard.surface_coefficients[3]) 241 | 242 | # maybe change to use proper formatting statements 243 | string = ( 244 | "TRC S" 245 | + str(SurfaceCard.surface_id) 246 | + " " 247 | + str(x) 248 | + " " 249 | + str(y) 250 | + " " 251 | + str(z) 252 | + " " 253 | + "0. " 254 | + str(h) 255 | + " 0. " 256 | + str(r) 257 | + " 0.0\n" 258 | ) 259 | # otherwise well change the surface to a GQ and call the correct 260 | # function 261 | else: 262 | coefficients = [0.0] * 10 263 | t = SurfaceCard.surface_coefficients[3] 264 | x_bar = SurfaceCard.surface_coefficients[0] 265 | y_bar = SurfaceCard.surface_coefficients[1] 266 | z_bar = SurfaceCard.surface_coefficients[2] 267 | coefficients[0] = 1.0 268 | coefficients[1] = t 269 | coefficients[2] = 1.0 270 | coefficients[3] = 0.0 271 | coefficients[4] = 0.0 272 | coefficients[5] = 0.0 273 | coefficients[6] = -2.0 * x_bar 274 | coefficients[7] = -2.0 * t * y_bar 275 | coefficients[8] = -2.0 * z_bar 276 | coefficients[9] = x_bar**2 + (t * y_bar**2) + z_bar**2 277 | SurfaceCard.surface_coefficients = coefficients 278 | string = fluka_gq(SurfaceCard) 279 | 280 | return string 281 | 282 | 283 | # fluka a cone along z 284 | def fluka_cone_z(SurfaceCard): 285 | string = "" 286 | # if the cone direction is specified 287 | if SurfaceCard.surface_coefficients[4] != 0.0: 288 | # cone points down from xyz 289 | if SurfaceCard.surface_coefficients[4] == -1: 290 | z = SurfaceCard.b_box[4] 291 | h = abs(SurfaceCard.b_box[4]) + SurfaceCard.surface_coefficients[2] 292 | # cone point up from xyz 293 | elif SurfaceCard.surface_coefficients[4] == 1: 294 | z = SurfaceCard.b_box[5] 295 | h = -1.0 * (SurfaceCard.b_box[5] - SurfaceCard.surface_coefficients[2]) 296 | 297 | x = SurfaceCard.surface_coefficients[0] 298 | y = SurfaceCard.surface_coefficients[1] 299 | r = abs(h) * sqrt(SurfaceCard.surface_coefficients[3]) 300 | 301 | # maybe change to use proper formatting statements 302 | string = ( 303 | "TRC S" 304 | + str(SurfaceCard.surface_id) 305 | + " " 306 | + str(x) 307 | + " " 308 | + str(y) 309 | + " " 310 | + str(z) 311 | + " " 312 | + "0. 0. " 313 | + str(h) 314 | + " " 315 | + str(r) 316 | + " 0.0\n" 317 | ) 318 | # otherwise well change the surface to a GQ and call the correct 319 | # function 320 | else: 321 | coefficients = [0.0] * 10 322 | t = SurfaceCard.surface_coefficients[3] 323 | x_bar = SurfaceCard.surface_coefficients[0] 324 | y_bar = SurfaceCard.surface_coefficients[1] 325 | z_bar = SurfaceCard.surface_coefficients[2] 326 | coefficients[0] = 1.0 327 | coefficients[1] = 1.0 328 | coefficients[2] = t 329 | coefficients[3] = 0.0 330 | coefficients[4] = 0.0 331 | coefficients[5] = 0.0 332 | coefficients[6] = -2.0 * x_bar 333 | coefficients[7] = -2.0 * y_bar 334 | coefficients[8] = -2.0 * t * z_bar 335 | coefficients[9] = x_bar**2 + y_bar**2 + (t * z_bar**2) 336 | SurfaceCard.surface_coefficients = coefficients 337 | string = fluka_gq(SurfaceCard) 338 | return string 339 | 340 | 341 | # maybe add auto expand torus to cones? 342 | # very few codes support tori due to numerical reasons 343 | # have previously had success defining torus as sets of cones 344 | # 345 | 346 | # fluka a torus x 347 | def fluka_torus_x(SurfaceCard): 348 | warnings.warn( 349 | "Warning tori are not supported in FLUKA, some manual manipulation will be required", 350 | Warning, 351 | ) 352 | string = "* Surface TORUS_X not supported\n" 353 | return string 354 | 355 | 356 | # fluka a torus y 357 | def fluka_torus_y(SurfaceCard): 358 | warnings.warn( 359 | "Warning tori are not supported in FLUKA, some manual manipulation will be required", 360 | Warning, 361 | ) 362 | string = "* Surface TORUS_Y not supported\n" 363 | return string 364 | 365 | 366 | # fluka a torus z 367 | def fluka_torus_z(SurfaceCard): 368 | warnings.warn( 369 | "Warning tori are not supported in FLUKA, some manual manipulation will be required", 370 | Warning, 371 | ) 372 | string = "* Surface TORUS_Z not supported\n" 373 | return string 374 | 375 | 376 | # write the surface description to file 377 | def write_fluka_surface(filestream, SurfaceCard): 378 | # NOTE this appears to be a nice way to get a pythonic case statement 379 | # is it equally ugly as below? 380 | # return { 381 | # SurfaceCard.SurfaceType["PLANE_GENERAL"]: surface_plane_write(Surface, 382 | # SurfaceCard.SurfaceType["CYLINDER_Y"]: "cylinder_y\n" 383 | # }.get(SurfaceCard.surface_type,"surface not supported") 384 | 385 | # string = "surf " + str(SurfaceCard.surface_id) 386 | string = "" 387 | if SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_GENERAL"]: 388 | string += fluka_plane_string(SurfaceCard) 389 | filestream.write(string) 390 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_X"]: 391 | string += fluka_plane_x_string(SurfaceCard) 392 | filestream.write(string) 393 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_Y"]: 394 | string += fluka_plane_y_string(SurfaceCard) 395 | filestream.write(string) 396 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_Z"]: 397 | string += fluka_plane_z_string(SurfaceCard) 398 | filestream.write(string) 399 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_X"]: 400 | string += fluka_cylinder_x(SurfaceCard) 401 | filestream.write(string) 402 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_Y"]: 403 | string += fluka_cylinder_y(SurfaceCard) 404 | filestream.write(string) 405 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_Z"]: 406 | string += fluka_cylinder_z(SurfaceCard) 407 | filestream.write(string) 408 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["SPHERE_GENERAL"]: 409 | string += fluka_sphere(SurfaceCard) 410 | filestream.write(string) 411 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_X"]: 412 | string += fluka_cone_x(SurfaceCard) 413 | filestream.write(string) 414 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_Y"]: 415 | string += fluka_cone_y(SurfaceCard) 416 | filestream.write(string) 417 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_Z"]: 418 | string += fluka_cone_z(SurfaceCard) 419 | filestream.write(string) 420 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["TORUS_X"]: 421 | string += fluka_torus_x(SurfaceCard) 422 | filestream.write(string) 423 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["TORUS_Y"]: 424 | string += fluka_torus_y(SurfaceCard) 425 | filestream.write(string) 426 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["TORUS_Z"]: 427 | string += fluka_torus_z(SurfaceCard) 428 | filestream.write(string) 429 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["GENERAL_QUADRATIC"]: 430 | string += fluka_gq(SurfaceCard) 431 | filestream.write(string) 432 | else: 433 | filestream.write("surface not supported\n") 434 | return 435 | 436 | 437 | # Surface Card Class 438 | class FLUKASurfaceCard(SurfaceCard): 439 | def __init__(self, card_string): 440 | SurfaceCard.__init__(self, card_string) 441 | 442 | def write(self): 443 | print("hello") 444 | -------------------------------------------------------------------------------- /csg2csg/Input.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | from csg2csg.UniverseCard import UniverseCard, uni_fill, mat_fill 3 | import copy 4 | 5 | class InputDeck: 6 | """InputDeck class from which other concrete examples 7 | should inherit, for example MCNPInputDeck will inherit 8 | from this class 9 | """ 10 | 11 | """ Constructor 12 | """ 13 | 14 | def __init__(self, filename, quick=False): 15 | self.filename = filename 16 | self.quick_process = quick 17 | 18 | self.file_lines = "" 19 | self.title = "" 20 | self.total_num_lines = 0 21 | 22 | # if doing a direct tranlation store here 23 | # TODO maybe these should be dictionaries by index 24 | self.cell_list = [] 25 | self.surface_list = [] 26 | self.universe_list = [] 27 | self.last_free_surface_index = 0 28 | self.importance_list = {} # dictionary of importances 29 | self.material_list = {} 30 | self.transform_list = {} 31 | 32 | # this calculates coordinates that bound the placement of 33 | # surfaces we do this by checking the manifold surfaces 34 | # for the their value and adding to the list 35 | self.bounding_coordinates = [0, 0, 0, 0, 0, 0] 36 | 37 | # if doing a hierachy transform store here 38 | self.cell_card_collection = {} 39 | self.surface_card_collection = {} 40 | 41 | # find the cell with a given id 42 | def find_cell(self, cell_id): 43 | for cell in self.cell_list: 44 | if str(cell.cell_id) == str(cell_id): 45 | return cell 46 | return None 47 | 48 | # read the whole file into a big list for further 49 | # procesing 50 | def read(self): 51 | with open(self.filename, errors="replace") as f: 52 | self.file_lines = f.readlines() 53 | 54 | # sometimes truely monstrous people stuff weird 55 | # characters into textfiles 56 | self.file_lines = [x.lower() for x in self.file_lines] 57 | 58 | self.total_num_lines = len(self.file_lines) 59 | 60 | # access a surface with a particular id 61 | def get_surface_with_id(self, id): 62 | for surface in self.surface_list: 63 | if surface.surface_id == id: 64 | return surface 65 | return None 66 | 67 | # access a cell with a particular id 68 | def get_cell_with_id(self, id): 69 | for cell in self.cell_list: 70 | if cell.cell_id == id: 71 | return cell 72 | return None 73 | 74 | # instanciate from input 75 | def from_input(self, InputDeckClass): 76 | self.filename = InputDeckClass.filename 77 | self.title = InputDeckClass.title 78 | self.cell_list = InputDeckClass.cell_list 79 | self.surface_list = InputDeckClass.surface_list 80 | self.material_list = InputDeckClass.material_list 81 | self.universe_list = InputDeckClass.universe_list 82 | return 83 | 84 | # step through each cell and determine if the cell can 85 | # be split into multiple simple subcells 86 | def split_unions(self): 87 | # update the cell definition - loop over all cells 88 | for idx, cell in enumerate(self.cell_list): 89 | # look through the interpreted cell and determine if we can pattern 90 | # match any splitable unions - this will look like (stuff):(stuff):(stuff) 91 | # so we could make 3 cells from that 92 | continue 93 | 94 | # prepare universe entries given cells 95 | def create_universes_from_cells(self): 96 | universe_ids = set() 97 | 98 | # count unique universe IDs 99 | universe_ids.update(int(cell.cell_universe) for cell in self.cell_list) 100 | 101 | # Maybe set is empty? In which case, put every cell in universe 1 102 | #if not universe_ids: 103 | # universe_ids.add(1) 104 | # for cell in self.cell_list: 105 | # cell.cell_universe = 1 106 | 107 | for uni in universe_ids: 108 | newUni = UniverseCard() 109 | newUni.build_from_cell_list(uni,self.cell_list) 110 | self.universe_list.append(newUni) 111 | 112 | # Need to ensure there is both a root universe and, if cells, 113 | # a cell universe to hold them. This may imply adding another 114 | # universe to the geometry and changing the cell universe IDs. 115 | # First need to identify whether the root universe has cells. 116 | # If so, duplicate the universe, make the new universe a cell 117 | # universe without being root, remove cells from the root universe, 118 | # and make the root universe contain the new universe. 119 | for uni in self.universe_list: 120 | if uni.is_root: 121 | if uni.cell_list: 122 | newID = max(universe_ids) + 1 123 | newUni = copy.copy(uni) 124 | newUni.universe_id = newID 125 | newUni.is_root = 0 126 | newUni.border_surface = 0 127 | uni.cell_list = [] 128 | uni.fill_type = uni_fill 129 | uni.fill_id = newID 130 | self.universe_list.append(newUni) 131 | 132 | return 133 | -------------------------------------------------------------------------------- /csg2csg/MCNPCellCard.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | from csg2csg.CellCard import CellCard 4 | from enum import Enum 5 | 6 | from csg2csg.MCNPFormatter import mcnp_line_formatter, get_fortran_formatted_number 7 | 8 | import re 9 | import math 10 | 11 | import logging 12 | 13 | # to support more keywords for cells add them here 14 | mcnp_cell_keywords = ["imp", "u", "fill", "vol", "tmp"] 15 | 16 | # if the string is a cell card or not 17 | def is_cell_card(line): 18 | cell_card = line.split() 19 | if len(cell_card) == 0: 20 | return False 21 | try: 22 | if any(s == cell_card[0][0] for s in ["(", ":", ")", "#"]): 23 | return False 24 | else: 25 | if int(cell_card[0]): 26 | return True 27 | except ValueError: 28 | return False 29 | 30 | cellid = int(cell_card[0]) 31 | mat_num = int(cell_card[1]) 32 | if cellid and mat_num == 0: 33 | return True 34 | elif cellid > 0 and mat_num > 0: 35 | try: 36 | float(cell_card[2]) 37 | except ValueError: 38 | print(cell_card[2], " cannot be converted to float") 39 | return True 40 | return False 41 | 42 | 43 | # turn the generic operation type into a mcnp relevant text string 44 | def mcnp_op_from_generic(Operation): 45 | # if we are not of type operator - we are string do nowt 46 | if not isinstance(Operation, CellCard.OperationType): 47 | if Operation == "(": 48 | return " " + Operation + " " 49 | elif Operation == ")": 50 | return " " + Operation + " " 51 | else: 52 | return Operation 53 | else: 54 | # otherwise we need to do something 55 | if Operation == CellCard.OperationType["NOT"]: 56 | string = " #" 57 | elif Operation == CellCard.OperationType["AND"]: 58 | string = " " 59 | elif Operation == CellCard.OperationType["UNION"]: 60 | string = " : " 61 | else: 62 | string = "unknown operation" 63 | # return the operation 64 | return string 65 | 66 | 67 | # write the cell card for a serpent cell given a generic cell card 68 | def write_mcnp_cell(filestream, CellCard, print_importances=True): 69 | string = str(CellCard.cell_id) + " " 70 | 71 | string += str(CellCard.cell_material_number) + " " 72 | if CellCard.cell_material_number != 0: 73 | string += str(CellCard.cell_density) + " " 74 | 75 | # string += " ( " 76 | 77 | # build the cell description 78 | for item in CellCard.cell_interpreted: 79 | string += mcnp_op_from_generic(item) 80 | 81 | # TODO make string no longer than 60 chars 82 | # string += " ) " 83 | string += "\n" 84 | 85 | string = re.sub(" +", " ", string) 86 | string = string.strip() 87 | 88 | if CellCard.cell_universe != 0: 89 | string += " u=" + CellCard.cell_universe 90 | 91 | if CellCard.cell_fill != 0: 92 | string += " fill=" + CellCard.cell_fill + " " 93 | if CellCard.cell_universe_offset != 0 or CellCard.cell_universe_rotation != 0: 94 | # universe may have no traslation? 95 | string += "(" 96 | if CellCard.cell_universe_offset != 0: 97 | for i in range(3): 98 | string += str(CellCard.cell_universe_offset[i]) + " " 99 | else: 100 | string += " 0 0 0 " 101 | 102 | if CellCard.cell_universe_rotation != 0: 103 | for i in range(9): 104 | value = float(CellCard.cell_universe_rotation[i]) 105 | # value = math.cos(value/180.*math.pi) 106 | string += str(value) + " " 107 | string += ")" 108 | 109 | if print_importances: 110 | string += " IMP:N=" + str(CellCard.cell_importance) 111 | 112 | string += "\n" 113 | string = mcnp_line_formatter(string) 114 | 115 | filestream.write(string) 116 | 117 | 118 | class MCNPCellCard(CellCard): 119 | """Class for the instanciation of generic cell cards 120 | from MCNP cell card strings 121 | """ 122 | 123 | # constructor 124 | def __init__(self, card_string): 125 | CellCard.__init__(self, card_string) 126 | self.__interpret() 127 | 128 | # check the cell text description for parentheses 129 | # or not symbols these need extra work 130 | def __is_sanitised(self): 131 | if "#" in self.cell_text_description: 132 | return False 133 | elif "(" or ")" in self.cell_text_description: 134 | return False 135 | else: 136 | return True 137 | 138 | # method to break mcnp cell definition into something 139 | # a mere mortal can understand turns mcnp description 140 | # like 2 3 -4 into 2 AND 3 AND -4 or even 2 AND 3 AND 141 | # +4 142 | def generalise(self): 143 | 144 | cell_description = self.cell_text_description 145 | # wholesale replace + signs as they are implicit 146 | # print(cell_description) 147 | # cell_description = [ s = "" for item in cell_description if item == "+"] 148 | cell_description = list(cell_description) 149 | 150 | idx = 0 151 | while True: 152 | s = cell_description[idx] 153 | if s == ":": 154 | cell_description[idx] = CellCard.OperationType["UNION"] 155 | idx += 1 156 | continue 157 | elif s == "#": 158 | cell_description[idx] = CellCard.OperationType["NOT"] 159 | idx += 1 160 | continue 161 | elif s == ("(" or ")"): 162 | idx += 1 163 | continue 164 | elif ( 165 | isinstance(s, str) 166 | and cell_description[idx - 1] != "(" 167 | and cell_description[idx] != ")" 168 | ): 169 | cell_description.insert(idx, CellCard.OperationType["AND"]) 170 | idx += 1 171 | try: 172 | surf_num = abs(int(s)) 173 | self.cell_surface_list.add(surf_num) 174 | except: 175 | pass # its means it was a macrobody 176 | 177 | idx += 1 178 | if idx == len(cell_description): 179 | break 180 | 181 | self.cell_interpreted = cell_description 182 | # print(self.cell_id) 183 | # print(self.cell_interpreted) 184 | # logging.debug("%s\n", "Generalised cell card " + ''.join([str(i) for i in self.cell_interpreted])) 185 | 186 | return 187 | 188 | # generally spaceify the text so that between each item 189 | # there is only one space i.e (7:8) becomes ( 7 : 8 ) 190 | def __sanitise(self): 191 | text = self.cell_text_description 192 | return 193 | 194 | # given a valid keyword and string return the value of the 195 | # keyword 196 | def __get_keyword_value(self, keyword, string): 197 | # regex = re.regex=re.compile("("+keyword+") ?= ?[1-9][0-9]*") 198 | regex = re.regex = re.compile( 199 | "(" + keyword + ") ?= ?(?=.)([+-]?([0-9]*)(\.([0-9]+))?)" 200 | ) 201 | result = regex.search(string)[0] 202 | return result.split(" ")[2] # string[offset:end] 203 | 204 | def __extract_string_between(self, string, first_substring, second_substring): 205 | # print(string, first_substring, second_substring,string.find(first_substring),string.find(second_substring)) 206 | pos1 = string.find(first_substring) + 1 207 | pos2 = string.find(second_substring) 208 | result = " ".join(string[pos1:pos2].split()) 209 | 210 | if pos1 == -1: 211 | return "" 212 | return result 213 | 214 | # look through the string for the keywords 215 | def __detect_keywords(self, keywords, string): 216 | 217 | # loop over the keywords and test 218 | found_keyword = False 219 | for word in keywords: 220 | if word in string: 221 | found_keyword = True 222 | break 223 | if not found_keyword: 224 | return string 225 | 226 | # 227 | posd = string.find("$") 228 | if posd != -1: 229 | string = string[:posd] 230 | 231 | # otherwise loop through and find 232 | # u= , fill=, imp and tmp: 233 | posu = string.find("u") 234 | posf = string.find("fill") 235 | post = string.find("tmp") 236 | 237 | # universe fill angle could be specified in degrees 238 | rot_angle_degrees = False 239 | if string.find("*fill") != -1: 240 | rot_angle_degrees = True 241 | posf -= 1 242 | 243 | posi = string.find("imp") 244 | posv = string.find("vol") 245 | 246 | # find the posititon of the first match 247 | positions = [posu, posf, posi, posv, post] 248 | if posu != -1 or posf != -1 or posi != -1 or posv != -1 or post != -1: 249 | m = min(i for i in positions if i > 0) 250 | else: 251 | return string 252 | 253 | # from the point m to the end of the string, spacify between = signs 254 | # remove multiple whitespace such that we have "keyword = value" 255 | end_of_string = string[m:].replace("=", " =") 256 | end_of_string = end_of_string.replace("=", "= ") 257 | end_of_string = end_of_string.replace(" ", " ") 258 | end_of_string += " " 259 | 260 | if posu == -1: 261 | self.cell_universe = 0 262 | else: 263 | self.cell_universe = self.__get_keyword_value("u", end_of_string) 264 | 265 | if posf == -1: 266 | self.cell_fill = 0 267 | else: 268 | self.cell_fill = self.__get_keyword_value("fill", end_of_string).strip() 269 | # if we have found fill, there may also be a rotation and translation 270 | # associated with the universe of the form (0 0 0) 271 | if "(" in string[posf:]: 272 | rot_trans = self.__extract_string_between(string[posf:], "(", ")") 273 | else: 274 | rot_trans = "0" 275 | 276 | self.__set_universe_transform(rot_trans, rot_angle_degrees) 277 | 278 | if posi == -1: 279 | self.cell_importance = 1.0 280 | else: 281 | self.cell_importance = float( 282 | self.__get_keyword_value("imp:n", end_of_string) 283 | ) 284 | 285 | # return the string upto the posisiotn of the first detected keyword 286 | return string[:m] 287 | 288 | # populute the part CellCard into its 289 | # consituent parts 290 | def __interpret(self): 291 | 292 | string = self.text_string 293 | # look for mcnp cell specific keywords 294 | string = self.__detect_keywords(mcnp_cell_keywords, string) 295 | 296 | # this is to detect the presence of any importance 297 | # values only need one - used to indentify the 298 | # graveyard 299 | 300 | # expand the string first 301 | string = string.replace("(", " ( ") 302 | string = string.replace(")", " ) ") 303 | string = string.replace(":", " : ") 304 | string = string.replace("+", "") # purge + signs 305 | 306 | # there can be mulitple comments per cell you sick sick people 307 | # why? is there any need? I mean really?! 308 | while "$" in string: 309 | pos = string.find("$") 310 | nl = string.find("\n", pos) 311 | string = string[:pos] + string[nl:] 312 | self.cell_comment += string[pos:nl] 313 | tokens = self.text_string.split() 314 | 315 | tokens = string.split() 316 | 317 | self.cell_id = int(tokens[0]) 318 | material_number = int(tokens[1]) 319 | if material_number > 0: 320 | self.cell_material_number = int(material_number) 321 | self.cell_density = get_fortran_formatted_number(tokens[2]) 322 | self.cell_text_description = tokens[3:] 323 | else: 324 | self.cell_density = 0.0 325 | self.cell_material_number = 0 326 | self.cell_text_description = tokens[2:] 327 | 328 | # now interpret the cell text description 329 | if not self.__is_sanitised(): 330 | self.__sanitise() 331 | self.generalise() 332 | 333 | return 334 | 335 | # set the universe transform 336 | # angle 337 | def __set_universe_transform(self, transform, angle_form_degrees): 338 | tokens = transform.split() 339 | for idx, i in enumerate(tokens): 340 | tokens[idx] = i 341 | 342 | # transform is a TR card 343 | if len(tokens) == 1: 344 | self.cell_universe_transformation_id = tokens[0] 345 | elif len(tokens) > 2: 346 | # set the offset 347 | self.cell_universe_offset = [tokens[0], tokens[1], tokens[2]] 348 | if len(tokens) > 11: 349 | rot_angles = [ 350 | tokens[3], 351 | tokens[4], 352 | tokens[5], 353 | tokens[6], 354 | tokens[7], 355 | tokens[8], 356 | tokens[9], 357 | tokens[10], 358 | tokens[11], 359 | ] 360 | # cannonical storage format is in radians 361 | if angle_form_degrees: 362 | for idx, angle in enumerate(rot_angles): 363 | rot_angles[idx] = math.cos(float(angle) / 180.0 * math.pi) 364 | self.cell_universe_rotation = rot_angles 365 | else: 366 | print("unknown method of transformation") 367 | return 368 | 369 | # apply the transform to the universe 370 | def apply_universe_transform(self, transform): 371 | self.cell_universe_offset = transform.shift # set the offset 372 | self.cell_universe_rotation = transform.v1 + transform.v2 + transform.v3 373 | # reset the transform 374 | self.cell_universe_transformation_id = "0" 375 | 376 | # update an existing cell description with 377 | def update(self, new_cell_description): 378 | # take the new cell description and make a new 379 | # cell description 380 | self.text_string = str(self.cell_id) 381 | self.text_string += " " + str(self.cell_material_number) 382 | if self.cell_material_number == 0: 383 | self.text_string += " " + new_cell_description 384 | else: 385 | self.text_string += " " + str(self.cell_density) 386 | self.text_string += " " + new_cell_description 387 | 388 | self.__interpret() 389 | -------------------------------------------------------------------------------- /csg2csg/MCNPDataCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | import sys 3 | from csg2csg.Card import Card 4 | from csg2csg.Vector import cross 5 | import math 6 | 7 | import warnings 8 | 9 | # Class to handle MCNP datacards 10 | class MCNPDataCard(Card): 11 | def __init__(self, card_string): 12 | Card.__init__(self, card_string) 13 | 14 | 15 | # Class to handle MCNP Transform Cards 16 | class MCNPTransformCard(MCNPDataCard): 17 | id = 0 # transform card number 18 | angle_form = 0 # 0 is radians, 1 is degrees 19 | # spatial shift 20 | shift = [0.0, 0.0, 0.0] 21 | # default basis vector 22 | v1 = [1.0, 0.0, 0.0] 23 | v2 = [0.0, 1.0, 0.0] 24 | v3 = [0.0, 0.0, 1.0] 25 | 26 | def __init__(self, card_string): 27 | MCNPDataCard.__init__(self, card_string) 28 | self.__process_string() 29 | 30 | def __str__(self): 31 | string = "transform: {0}\n".format(self.id) 32 | string += "shift: {0}\n".format(self.shift) 33 | string += "v1: {0}\n".format(self.v1) 34 | string += "v2: {0}\n".format(self.v2) 35 | string += "v3: {0}\n".format(self.v3) 36 | return string 37 | 38 | def set_shift(self, shift_): 39 | self.shift = shift_ 40 | 41 | # process the string into a transformation card 42 | def __process_string(self): 43 | tokens = self.text_string.split() 44 | 45 | # is the angle specificed in rads or degrees 46 | if "*" in tokens[0]: 47 | self.angle_form = 1 48 | else: 49 | self.angle_form = 0 50 | # id string 51 | id_string = tokens[0].find("r") + 1 52 | self.id = tokens[0][id_string:] 53 | # the xyz shift of the tform 54 | self.shift = [float(tokens[1]), float(tokens[2]), float(tokens[3])] 55 | 56 | if len(tokens) == 13 or len(tokens) == 14: # fully defined transform 57 | self.v1 = [float(tokens[4]), float(tokens[5]), float(tokens[6])] 58 | self.v2 = [float(tokens[7]), float(tokens[8]), float(tokens[9])] 59 | self.v3 = [float(tokens[10]), float(tokens[11]), float(tokens[12])] 60 | 61 | # convert from degs to radians 62 | if self.angle_form: 63 | for i in range(3): 64 | self.v1[i] = math.cos(self.v1[i] / 180.0 * math.pi) 65 | self.v2[i] = math.cos(self.v2[i] / 180.0 * math.pi) 66 | self.v3[i] = math.cos(self.v3[i] / 180.0 * math.pi) 67 | elif len(tokens) == 10: # define the las transform as cross product 68 | self.v1 = [float(tokens[4]), float(tokens[5]), float(tokens[6])] 69 | self.v2 = [float(tokens[7]), float(tokens[8]), float(tokens[9])] 70 | # convert from degs to radians 71 | if self.angle_form: 72 | for i in range(3): 73 | self.v1[i] = math.cos(self.v1[i] / 180.0 * math.pi) 74 | self.v2[i] = math.cos(self.v2[i] / 180.0 * math.pi) 75 | self.v3 = cross(self.v1, self.v2) 76 | elif len(tokens) == 4: # just a translation 77 | # shift already set and unitary rotation matrix 78 | # defined 79 | pass 80 | else: 81 | warnings.warn( 82 | "Unknown transform definition, " + str(len(tokens)) + self.text_string, 83 | Warning, 84 | ) 85 | 86 | return 87 | -------------------------------------------------------------------------------- /csg2csg/MCNPFormatter.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | # given a string find a dollar comment '$' and 4 | # return the string upto that point 5 | def strip_dollar_comments(string): 6 | pos = string.find("$") 7 | if pos == -1: 8 | return string 9 | 10 | return string[0:pos] 11 | 12 | 13 | # given a fortran formatted number return 14 | # it as a float 15 | def get_fortran_formatted_number(string): 16 | 17 | new_string = string.split(".") 18 | # now have something like [2][321455] 19 | if len(new_string) == 1: # no decimal point 20 | return float(string) 21 | 22 | # its a normal number 23 | if any(item in new_string[1].lower() for item in ["e-", "e+", "e"]): 24 | return float(string) 25 | 26 | # its some weird old style fortran formatted number 27 | new_string[1] = new_string[1].replace("-", "e-") 28 | new_string[1] = new_string[1].replace("+", "e+") 29 | 30 | return float(new_string[0] + "." + new_string[1]) 31 | 32 | 33 | # take a massive string for an MCNP cell line 34 | # makes it no more than 80 chars wide and 35 | # include the right indentation 36 | def mcnp_line_formatter(string_to_format): 37 | tmp_string = string_to_format 38 | # early return string already ok 39 | if len(tmp_string) < 72: 40 | return tmp_string 41 | else: 42 | # need to loop until string is finished 43 | new_string = "" 44 | while True: 45 | # to do make line length an argument? 46 | if len(tmp_string) <= 72: 47 | if not tmp_string.isspace(): 48 | new_string += tmp_string 49 | break 50 | else: 51 | # need to not chop text without disturbing 52 | # underlying definition - find first space 53 | # reverse search and split there 54 | pos = tmp_string[:72].rfind(" ") 55 | # todo - robustify this it must be possible for there 56 | # to be no space in the string 57 | new_string += tmp_string[:pos] + "\n" 58 | tmp_string = tmp_string[pos:] 59 | # if remaining string is empty just leave 60 | if tmp_string.isspace(): 61 | return new_string 62 | else: 63 | # if we are continuing add spaces 64 | new_string += " " 65 | 66 | return new_string 67 | -------------------------------------------------------------------------------- /csg2csg/MCNPMaterialCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.MaterialCard import MaterialCard 4 | from csg2csg.MCNPFormatter import get_fortran_formatted_number 5 | 6 | import sys 7 | 8 | # writes an mcnp material card given the generic description 9 | def write_mcnp_material(filestream, Material, preserve_xs, write_comment=True): 10 | 11 | # if you want the comment 12 | if write_comment: 13 | filestream.write("C Material " + str(Material.material_name) + "\n") 14 | 15 | filestream.write("M" + str(Material.material_number)) 16 | for nucid in Material.composition_dictionary: 17 | string = " " + str(nucid) 18 | if preserve_xs: 19 | string += "." + str(Material.xsid_dictionary[nucid]) 20 | string += " " + str(Material.composition_dictionary[nucid]) + "\n" 21 | filestream.write(string) 22 | return 23 | 24 | 25 | # Class to handle MCNP Material Card 26 | class MCNPMaterialCard(MaterialCard): 27 | def __init__(self, material_number, card_string): 28 | MaterialCard.__init__(self, material_number, card_string) 29 | self.material_name = "M" + str(material_number) 30 | self.material_number = material_number 31 | self.__process_string() 32 | 33 | # populate the MCNP Material Card 34 | def __process_string(self): 35 | # need to reset the dictionary 36 | # otherwise state seems to linger - weird 37 | self.composition_dictionary = {} 38 | 39 | mat_string = self.text_string 40 | mat_string = mat_string.replace("\n", "") 41 | 42 | # split string 43 | all_tokens = mat_string.split() 44 | 45 | # need to remove other material keywords not zaids 46 | tokens = [] 47 | keywords = [] 48 | for token in all_tokens: 49 | if "=" in token: 50 | keywords.append(token) 51 | else: 52 | tokens.append(token) 53 | 54 | if len(tokens) % 2 != 0: 55 | print("Material string not correctly processed") 56 | sys.exit(1) 57 | while len(tokens) != 0: 58 | nuclide = tokens[0].split(".") 59 | nucid = nuclide[0] 60 | try: 61 | xsid = nuclide[1] 62 | except: 63 | xsid = "" 64 | 65 | frac = get_fortran_formatted_number(tokens[1]) 66 | tokens.pop(0) 67 | tokens.pop(0) 68 | if nucid in self.composition_dictionary.keys(): 69 | self.composition_dictionary[nucid] += frac 70 | else: 71 | self.composition_dictionary[nucid] = frac 72 | self.xsid_dictionary[nucid] = xsid 73 | return 74 | -------------------------------------------------------------------------------- /csg2csg/MCNPParticleNames.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.ParticleNames import ParticleNames 4 | 5 | 6 | def particleToMCNP(particle_name): 7 | if particle_name == ParticleNames["NEUTRON"]: 8 | return "n" 9 | if particle_name == ParticleNames["PHOTON"]: 10 | return "p" 11 | if particle_name == ParticleNames["ELECTRON"]: 12 | return "e" 13 | if particle_name == ParticleNames["POSITRON"]: 14 | return "f" 15 | if particle_name == ParticleNames["PROTON"]: 16 | return "h" 17 | if particle_name == ParticleNames["DEUTERON"]: 18 | return "d" 19 | if particle_name == ParticleNames["TRITON"]: 20 | return "t" 21 | if particle_name == ParticleNames["ALPHA"]: 22 | return "a" 23 | if particle_name == ParticleNames["PION_PLUS"]: 24 | return "/" 25 | if particle_name == ParticleNames["PION_NEUT"]: 26 | return "z" 27 | if particle_name == ParticleNames["PION_NEG"]: 28 | return "*" 29 | if particle_name == ParticleNames["HELION"]: 30 | return "s" 31 | if particle_name == ParticleNames["MUON_NEG"]: 32 | return "|" 33 | 34 | 35 | def mcnpToParticle(particle_name): 36 | if particle_name == "n": 37 | return ParticleNames["NEUTRON"] 38 | if particle_name == "p": 39 | return ParticleNames["PHOTON"] 40 | if particle_name == "e": 41 | return ParticleNames["ELECTRON"] 42 | if particle_name == "f": 43 | return ParticleNames["POSITRON"] 44 | if particle_name == "h": 45 | return ParticleNames["PROTON"] 46 | if particle_name == "d": 47 | return ParticleNames["DEUTERON"] 48 | if particle_name == "t": 49 | return ParticleNames["TRITON"] 50 | if particle_name == "a": 51 | return ParticleNames["ALPHA"] 52 | if particle_name == "/": 53 | return ParticleNames["PION_PLUS"] 54 | if particle_name == "z": 55 | return ParticleNames["PION_NEUT"] 56 | if particle_name == "*": 57 | return ParticleNames["PION_NEG"] 58 | if particle_name == "s": 59 | return ParticleNames["HELION"] 60 | if particle_name == "|": 61 | return ParticleNames["MUON_NEG"] 62 | -------------------------------------------------------------------------------- /csg2csg/MaterialCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.Card import Card 4 | from csg2csg.MaterialData import MaterialData 5 | 6 | colours = [0] * 71 7 | colours[0] = "0 208 31" 8 | colours[1] = "0 0 255" 9 | colours[2] = "255 255 0" 10 | colours[3] = "0 255 0" 11 | colours[4] = "0 255 255" 12 | colours[5] = "255 164 0" 13 | colours[6] = "255 192 202" 14 | colours[7] = "159 31 239" 15 | colours[8] = "164 42 42" 16 | colours[9] = "111 128 144" 17 | colours[10] = "239 255 255" 18 | colours[11] = "222 184 134" 19 | colours[12] = "126 255 0" 20 | colours[13] = "255 0 255" 21 | colours[14] = "255 126 80" 22 | colours[15] = "255 248 220" 23 | colours[16] = "177 33 33" 24 | colours[17] = "255 214 0" 25 | colours[18] = "239 255 239" 26 | colours[19] = "239 230 139" 27 | colours[20] = "175 47 95" 28 | colours[21] = "218 111 213" 29 | colours[22] = "218 164 31" 30 | colours[23] = "221 159 221" 31 | colours[24] = "255 245 237" 32 | colours[25] = "159 82 44" 33 | colours[26] = "215 190 215" 34 | colours[27] = "255 98 70" 35 | colours[28] = "64 223 208" 36 | colours[29] = "245 222 179" 37 | colours[30] = "249 128 113" 38 | colours[31] = "95 158 159" 39 | colours[32] = "184 133 10" 40 | colours[33] = "84 107 46" 41 | colours[34] = "106 90 205" 42 | colours[35] = "255 139 0" 43 | colours[36] = "153 49 204" 44 | colours[37] = "143 187 143" 45 | colours[38] = "46 79 79" 46 | colours[39] = "255 19 146" 47 | colours[40] = "0 190 255" 48 | colours[41] = "249 235 214" 49 | colours[42] = "255 239 245" 50 | colours[43] = "172 215 230" 51 | colours[44] = "237 221 130" 52 | colours[45] = "255 182 193" 53 | colours[46] = "30 144 255" 54 | colours[47] = "255 159 121" 55 | colours[48] = "134 206 249" 56 | colours[49] = "255 255 223" 57 | colours[50] = "185 84 210" 58 | colours[51] = "175 196 222" 59 | colours[52] = "146 111 219" 60 | colours[53] = "255 69 0" 61 | colours[54] = "151 250 151" 62 | colours[55] = "174 237 237" 63 | colours[56] = "219 111 146" 64 | colours[57] = "223 255 255" 65 | colours[58] = "65 105 224" 66 | colours[59] = "187 143 143" 67 | colours[60] = "134 206 235" 68 | colours[61] = "0 255 126" 69 | colours[62] = "70 130 180" 70 | colours[63] = "255 0 0" 71 | colours[64] = "0 0 0" 72 | colours[65] = "0 0 255" 73 | colours[66] = "191 0 0" 74 | colours[67] = "0 255 0" 75 | colours[68] = "191 0 255" 76 | colours[69] = "255 255 0" 77 | colours[70] = "255 255 255" 78 | 79 | # get the mcnp colour for the material 80 | # this is mostly for automated testing 81 | def get_material_colour(idx): 82 | # this obviously returns the same colour more than once 83 | # but this is what MCNP does so we duplicate this behavior 84 | return colours[idx % 63] 85 | 86 | 87 | class MaterialCard(Card): 88 | 89 | """A fully defined material card should have a name, a material number, 90 | a density, a composition dictionary, and optionally a xsid dictionary. 91 | MCNP being an exception, most MC codes define the density of a material 92 | belonging to the material definition as opposed to a given cell. This 93 | approach is taken here for maximal compability amongst codes. 94 | """ 95 | 96 | # constructor 97 | def __init__(self, material_number=0, card_string=""): 98 | Card.__init__(self, card_string) 99 | self.material_name = "" 100 | self.material_number = 0 101 | self.composition_dictionary = {} 102 | self.xsid_dictionary = {} 103 | self.density = 0 104 | self.mat_data = 0 105 | self.material_colour = 0 106 | self.material_number = material_number 107 | self.mat_data = MaterialData() 108 | 109 | def __str__(self): 110 | string = "Material: " + self.material_name + "\n" 111 | string += "Material num: " + str(self.material_number) + "\n" 112 | string += "Density: " + str(self.density) + "\n" 113 | string += "Colour: " + str(self.material_colour) + "\n" 114 | string += "Composition \n" 115 | for item in self.composition_dictionary.keys(): 116 | string += item + " " + str(self.composition_dictionary[item]) + "\n" 117 | 118 | return string 119 | 120 | # normalise the material composition such that the sum is 1.0 121 | def normalise(self): 122 | sum = 0.0 123 | # get the sum 124 | for nuc in self.composition_dictionary: 125 | sum += float(self.composition_dictionary[nuc]) 126 | 127 | # dont divide by -ve number ! mass->atom 128 | sum = abs(sum) 129 | 130 | for nuc in self.composition_dictionary: 131 | self.composition_dictionary[nuc] = ( 132 | float(self.composition_dictionary[nuc]) / sum 133 | ) 134 | 135 | # all done 136 | return 137 | 138 | # explode elements loop through the dictionary and any material that has elements 139 | # and explode it into its nuclidewise definition 140 | def explode_elements(self): 141 | keys_to_remove = [] 142 | new_nuclides = {} 143 | for nuc in self.composition_dictionary: 144 | if int(nuc) % 1000 == 0: 145 | keys_to_remove.append(nuc) 146 | nuclides = self.mat_data.get_nucs(int(nuc)) 147 | # loop over the nuclides 148 | for nuclide in nuclides: 149 | if ( 150 | self.composition_dictionary[nuc] < 0 151 | ): # if its mass fraction then 152 | new_nuclides[str(nuclide)] = ( 153 | self.composition_dictionary[nuc] 154 | * self.mat_data.natural_abund_map[nuclide * 10000] 155 | / 100 156 | * self.mat_data.atomic_mass(int(nuc)) 157 | / self.mat_data.get_aa(nuclide) 158 | ) 159 | else: # its atom fraction pure multiplication 160 | new_nuclides[str(nuclide)] = ( 161 | self.composition_dictionary[nuc] 162 | * self.mat_data.natural_abund_map[nuclide * 10000] 163 | / 100 164 | ) 165 | 166 | # print(self.composition_dictionary) 167 | for key in keys_to_remove: 168 | del self.composition_dictionary[key] 169 | 170 | for key in new_nuclides.keys(): 171 | self.composition_dictionary[key] = new_nuclides[key] 172 | self.xsid_dictionary[key] = "" 173 | 174 | -------------------------------------------------------------------------------- /csg2csg/OpenMCCell.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | import math 4 | import numpy as np 5 | 6 | from csg2csg.CellCard import CellCard 7 | import xml.etree.ElementTree as ET 8 | 9 | 10 | def angle_from_rotmatrix(matrix): 11 | 12 | matrix = [float(i) for i in matrix] 13 | # from https://www.learnopencv.com/rotation-matrix-to-euler-angles/ 14 | sy = math.sqrt(matrix[0] ** 2 + matrix[3] ** 2) 15 | singular = sy < 1.0e-6 16 | if not singular: 17 | phi = math.atan2(matrix[7], matrix[8]) 18 | theta = math.atan2(-matrix[6], sy) 19 | # note certainly it seems this leads to the opposite solution 20 | psi = -1 * math.atan2(matrix[3], matrix[0]) 21 | else: 22 | phi = math.atan2(-matrix[5], matrix[4]) 23 | theta = math.atan2(-matrix[6], sy) 24 | psi = 0 25 | 26 | phi = np.rad2deg(phi) 27 | theta = np.rad2deg(theta) 28 | psi = np.rad2deg(psi) 29 | 30 | return (phi, theta, psi) 31 | 32 | 33 | def rotmatrix_from_angle(angles): 34 | angles = [float(i) for i in angles] 35 | 36 | psi = angles[2] / 180 * math.pi 37 | theta = angles[1] / 180 * math.pi 38 | phi = angles[0] / 180 * math.pi 39 | 40 | rotation_matrix = [0.0] * 9 41 | 42 | rotation_matrix[0] = math.cos(theta) * math.cos(psi) 43 | rotation_matrix[1] = -math.cos(phi) * math.sin(psi) + math.sin(phi) * math.sin( 44 | theta 45 | ) * math.cos(psi) 46 | rotation_matrix[2] = math.sin(phi) * math.sin(psi) + math.cos(phi) * math.sin( 47 | theta 48 | ) * math.cos(psi) 49 | rotation_matrix[3] = math.cos(theta) * math.sin(psi) 50 | rotation_matrix[4] = math.cos(phi) * math.cos(psi) + math.sin(phi) * math.sin( 51 | theta 52 | ) * math.sin(psi) 53 | rotation_matrix[5] = -math.sin(phi) * math.cos(psi) + math.cos(phi) * math.sin( 54 | theta 55 | ) * math.sin(psi) 56 | rotation_matrix[6] = -math.sin(theta) 57 | rotation_matrix[7] = math.sin(phi) * math.cos(theta) 58 | rotation_matrix[8] = math.cos(phi) * math.cos(theta) 59 | 60 | return rotation_matrix 61 | 62 | 63 | # turn the generic operation type into an openmc relevant text string 64 | def openmc_op_from_generic(Operation): 65 | # if we are not of type operator - we are string do nowt 66 | if not isinstance(Operation, CellCard.OperationType): 67 | return Operation 68 | else: 69 | # otherwise we need to do something 70 | if Operation is CellCard.OperationType["NOT"]: 71 | string = " ~ " 72 | elif Operation is CellCard.OperationType["AND"]: 73 | string = " " 74 | elif Operation is CellCard.OperationType["UNION"]: 75 | string = " | " 76 | else: 77 | string = "unknown operation" 78 | # return the operation 79 | return string 80 | 81 | 82 | # generate the strings that define the xml version of the 83 | # cell 84 | def get_openmc_cell_info(cell): 85 | cell_id = str(cell.cell_id) 86 | material_number = str(cell.cell_material_number) 87 | 88 | if material_number == "0": 89 | material_number = "void" 90 | 91 | # make the string to define the cell 92 | operation = "".join(openmc_op_from_generic(e) for e in cell.cell_interpreted) 93 | operation = "".join(str(e) for e in operation) 94 | operation = operation.replace("(", " ( ") 95 | operation = operation.replace(")", " ) ") 96 | universe = cell.cell_universe 97 | fill = cell.cell_fill 98 | 99 | if cell.cell_universe_rotation != 0: 100 | rotation = "" 101 | 102 | rotation = "{} {} {} {} {} {} {} {} {}".format( 103 | cell.cell_universe_rotation[0], 104 | cell.cell_universe_rotation[1], 105 | cell.cell_universe_rotation[2], 106 | cell.cell_universe_rotation[3], 107 | cell.cell_universe_rotation[4], 108 | cell.cell_universe_rotation[5], 109 | cell.cell_universe_rotation[6], 110 | cell.cell_universe_rotation[7], 111 | cell.cell_universe_rotation[8], 112 | ) 113 | 114 | # [phi,theta,psi] = angle_from_rotmatrix(cell.cell_universe_rotation) 115 | # """ 116 | # print (cell.cell_id, phi, theta, psi) 117 | 118 | # rotation += str(phi) + " " 119 | # rotation += str(theta) + " " 120 | # rotation += str(psi) 121 | else: 122 | rotation = "0 0 0" 123 | 124 | if cell.cell_universe_offset != 0: 125 | translation = "" 126 | translation += str(cell.cell_universe_offset[0]) + " " 127 | translation += str(cell.cell_universe_offset[1]) + " " 128 | translation += str(cell.cell_universe_offset[2]) + " " 129 | else: 130 | translation = "0 0 0" 131 | 132 | return (cell_id, material_number, operation, universe, fill, rotation, translation) 133 | 134 | 135 | def write_openmc_cell(cell, geometry_tree): 136 | 137 | ( 138 | cell_id, 139 | material_number, 140 | description, 141 | universe, 142 | fill, 143 | rotation, 144 | translation, 145 | ) = get_openmc_cell_info(cell) 146 | 147 | if fill != 0: 148 | ET.SubElement( 149 | geometry_tree, 150 | "cell", 151 | id=str(cell_id), 152 | region=str(description), 153 | universe=str(universe), 154 | fill=str(fill), 155 | rotation=str(rotation), 156 | translation=str(translation), 157 | ) 158 | else: 159 | ET.SubElement( 160 | geometry_tree, 161 | "cell", 162 | id=str(cell_id), 163 | material=str(material_number), 164 | region=str(description), 165 | universe=str(universe), 166 | ) 167 | 168 | 169 | # 170 | class OpenMCCell(CellCard): 171 | def __init__(self, card_string): 172 | CellCard.__init__(self, card_string) 173 | -------------------------------------------------------------------------------- /csg2csg/OpenMCInput.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | from csg2csg.Input import InputDeck 4 | 5 | import logging 6 | import sys 7 | import xml.etree.ElementTree as ET 8 | 9 | from csg2csg.OpenMCSurface import write_openmc_surface 10 | from csg2csg.OpenMCCell import write_openmc_cell 11 | from csg2csg.OpenMCMaterial import write_openmc_material 12 | 13 | """ 14 | copy and paste from http://effbot.org/zone/element-lib.htm#prettyprint 15 | it basically walks your tree and adds spaces and newlines so the tree is 16 | printed in a nice way 17 | """ 18 | 19 | 20 | def indent(elem, level=0): 21 | i = "\n" + level * " " 22 | if len(elem): 23 | if not elem.text or not elem.text.strip(): 24 | elem.text = i + " " 25 | if not elem.tail or not elem.tail.strip(): 26 | elem.tail = i 27 | for elem in elem: 28 | indent(elem, level + 1) 29 | if not elem.tail or not elem.tail.strip(): 30 | elem.tail = i 31 | else: 32 | if level and (not elem.tail or not elem.tail.strip()): 33 | elem.tail = i 34 | 35 | 36 | class OpenMCInput(InputDeck): 37 | """OpenMCInputDeck intrastructure to write and OpenMC input deck 38 | by not invoking the python binding for OpenMC, but writing the 39 | xml elements directly 40 | """ 41 | 42 | # constructor 43 | def __init__(self, filename=""): 44 | InputDeck.__init__(self, filename) 45 | 46 | # write the collection of OpenMC surface definitions 47 | def __write_openmc_surfaces(self, geometry_tree): 48 | for surface in self.surface_list: 49 | write_openmc_surface(surface, geometry_tree) 50 | 51 | # write the collection of OpenMC cell definitions 52 | def __write_openmc_cells(self, geometry_tree): 53 | for cell in self.cell_list: 54 | write_openmc_cell(cell, geometry_tree) 55 | 56 | # write the collection of Material 57 | def __write_openmc_materials(self, material_tree): 58 | for mat in self.material_list: 59 | write_openmc_material(self.material_list[mat], material_tree) 60 | 61 | def __get_unused_fills(self, geometry_tree): 62 | universes = set() 63 | fill_universes = set() 64 | 65 | for cell_elem in geometry_tree.findall("cell"): 66 | u = int(cell_elem.get("universe")) 67 | universes.add(u) 68 | 69 | if "fill" in cell_elem.keys(): 70 | fill = int(cell_elem.get("fill")) 71 | fill_universes.add(fill) 72 | 73 | for lattice_elem in geometry_tree.findall("lattice"): 74 | u = int(lattice_elem.get("id")) 75 | universes.add(u) 76 | for u in lattice_elem.find("universes").text.split(): 77 | fill_universes.add(int(u)) 78 | 79 | not_used_universerses = universes - fill_universes 80 | 81 | return not_used_universerses 82 | 83 | # check to see if any universes are unsed 84 | def __check_unused_universes(self, geometry_tree): 85 | # loop over the unsed fills 86 | while len(self.__get_unused_fills(geometry_tree)) > 1: 87 | cells_to_remove = set() 88 | # loop over the universes not used as a fill 89 | for universe in self.__get_unused_fills(geometry_tree): 90 | if universe != 0: 91 | for cell_elem in geometry_tree.findall("cell"): 92 | u = int(cell_elem.get("universe")) 93 | if u == universe: 94 | cells_to_remove.add(cell_elem) 95 | 96 | # loop over the cells that have not fill 97 | for cell in cells_to_remove: 98 | geometry_tree.remove(cell) 99 | 100 | return 101 | 102 | # write the openmc geometry 103 | def write_openmc(self, filename, flat=True): 104 | geometry = ET.Element("geometry") 105 | 106 | self.__write_openmc_surfaces(geometry) 107 | self.__write_openmc_cells(geometry) 108 | self.__check_unused_universes(geometry) 109 | 110 | tree = ET.ElementTree(geometry) 111 | indent(geometry) 112 | tree.write(filename + "/geometry.xml") 113 | 114 | # write the materials 115 | material_tree = ET.Element("materials") 116 | tree = ET.ElementTree(material_tree) 117 | self.__write_openmc_materials(material_tree) 118 | indent(material_tree) 119 | tree.write(filename + "/materials.xml") 120 | -------------------------------------------------------------------------------- /csg2csg/OpenMCMaterial.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.MaterialCard import MaterialCard 4 | import xml.etree.ElementTree as ET 5 | 6 | name_zaid = { 7 | 1: "H", 8 | 2: "He", 9 | 3: "Li", 10 | 4: "Be", 11 | 5: "B", 12 | 6: "C", 13 | 7: "N", 14 | 8: "O", 15 | 9: "F", 16 | 10: "Ne", 17 | 11: "Na", 18 | 12: "Mg", 19 | 13: "Al", 20 | 14: "Si", 21 | 15: "P", 22 | 16: "S", 23 | 17: "Cl", 24 | 18: "Ar", 25 | 19: "K", 26 | 20: "Ca", 27 | 21: "Sc", 28 | 22: "Ti", 29 | 23: "V", 30 | 24: "Cr", 31 | 25: "Mn", 32 | 26: "Fe", 33 | 27: "Co", 34 | 28: "Ni", 35 | 29: "Cu", 36 | 30: "Zn", 37 | 31: "Ga", 38 | 32: "Ge", 39 | 33: "As", 40 | 34: "Se", 41 | 35: "Br", 42 | 36: "Kr", 43 | 37: "Rb", 44 | 38: "Sr", 45 | 39: "Y", 46 | 40: "Zr", 47 | 41: "Nb", 48 | 42: "Mo", 49 | 43: "Tc", 50 | 44: "Ru", 51 | 45: "Rh", 52 | 46: "Pd", 53 | 47: "Ag", 54 | 48: "Cd", 55 | 49: "In", 56 | 50: "Sn", 57 | 51: "Sb", 58 | 52: "Te", 59 | 53: "I", 60 | 54: "Xe", 61 | 55: "Cs", 62 | 56: "Ba", 63 | 57: "La", 64 | 58: "Ce", 65 | 59: "Pr", 66 | 60: "Nd", 67 | 61: "Pm", 68 | 62: "Sm", 69 | 63: "Eu", 70 | 64: "Gd", 71 | 65: "Tb", 72 | 66: "Dy", 73 | 67: "Ho", 74 | 68: "Er", 75 | 69: "Tm", 76 | 70: "Yb", 77 | 71: "Lu", 78 | 72: "Hf", 79 | 73: "Ta", 80 | 74: "W", 81 | 75: "Re", 82 | 76: "Os", 83 | 77: "Ir", 84 | 78: "Pt", 85 | 79: "Au", 86 | 80: "Hg", 87 | 81: "Tl", 88 | 82: "Pb", 89 | 83: "Bi", 90 | 84: "Po", 91 | 85: "At", 92 | 86: "Rn", 93 | 87: "Fr", 94 | 88: "Ra", 95 | 89: "Ac", 96 | 90: "Th", 97 | 91: "Pa", 98 | 92: "U", 99 | 93: "Np", 100 | 94: "Pu", 101 | 95: "Am", 102 | 96: "Cm", 103 | 97: "Bk", 104 | 98: "Cf", 105 | 99: "Es", 106 | 100: "Fm", 107 | 101: "Md", 108 | 102: "No", 109 | 103: "Lr", 110 | 104: "Rf", 111 | 105: "Db", 112 | 106: "Sg", 113 | 107: "Bh", 114 | 108: "Hs", 115 | 109: "Mt", 116 | 110: "Ds", 117 | 111: "Rg", 118 | 112: "Cn", 119 | 113: "Nh", 120 | 114: "Fl", 121 | 115: "Mc", 122 | 116: "Lv", 123 | 117: "Ts", 124 | 118: "Og", 125 | } 126 | 127 | # convert zaid to a name for openmc 128 | def zaid_to_name(zaid_string): 129 | if len(zaid_string) <= 4: 130 | zz = int(zaid_string[0]) 131 | aa = int(zaid_string[2:]) 132 | elif len(zaid_string) > 5: 133 | zz = int(zaid_string[0:3]) 134 | aa = int(zaid_string[3:]) 135 | else: 136 | zz = int(zaid_string[0:2]) 137 | aa = int(zaid_string[2:]) 138 | 139 | # turn zz into a name 140 | 141 | name = name_zaid[zz] 142 | return name + str(aa) 143 | 144 | 145 | # write the atomic fraction entry 146 | def __write_atomic_fraction(material, nuclide, mass_frac): 147 | ET.SubElement(material, "nuclide", name=nuclide, ao=str(abs(mass_frac))) 148 | return 149 | 150 | 151 | # write a mass fraction entry 152 | def __write_mass_fraction(material, nuclide, mass_frac): 153 | ET.SubElement(material, "nuclide", name=nuclide, wo=str(abs(mass_frac))) 154 | return 155 | 156 | 157 | # generate the information required to write the 158 | # material xml element 159 | def write_openmc_material(MaterialCard, material_tree): 160 | matid = str(MaterialCard.material_number) 161 | matname = str(MaterialCard.material_name) 162 | density = str(abs(MaterialCard.density)) 163 | if MaterialCard.density < 0: 164 | density_units = "g/cc" 165 | else: 166 | density_units = "atom/b-cm" 167 | 168 | material = ET.SubElement(material_tree, "material", id=matid, name=matname) 169 | ET.SubElement(material, "density", value=density, units=density_units) 170 | for nuclide in MaterialCard.composition_dictionary: 171 | mass_frac = MaterialCard.composition_dictionary[nuclide] 172 | nuclide_name = zaid_to_name(nuclide) 173 | if mass_frac < 0: 174 | __write_mass_fraction(material, nuclide_name, mass_frac) 175 | else: 176 | __write_atomic_fraction(material, nuclide_name, mass_frac) 177 | -------------------------------------------------------------------------------- /csg2csg/OpenMCSurface.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.SurfaceCard import SurfaceCard 4 | import xml.etree.ElementTree as ET 5 | 6 | import warnings 7 | 8 | 9 | def boundary_condition(boundaryCondition): 10 | if boundaryCondition == SurfaceCard.BoundaryCondition["TRANSMISSION"]: 11 | boundary = "transmission" 12 | if boundaryCondition == SurfaceCard.BoundaryCondition["VACUUM"]: 13 | boundary = "vacuum" 14 | if boundaryCondition == SurfaceCard.BoundaryCondition["REFLECTING"]: 15 | boundary = "reflecting" 16 | if boundaryCondition == SurfaceCard.BoundaryCondition["WHITE"]: 17 | boundary = "vacuum" 18 | warnings.warn( 19 | "Found an unsupported boundary condition for OpenMC, White boundary considered vacuum", 20 | Warning, 21 | ) 22 | 23 | return boundary 24 | 25 | 26 | def openmc_surface_info(SurfaceCard): 27 | if SurfaceCard.surface_type == SurfaceCard.SurfaceType["PLANE_GENERAL"]: 28 | type_string = "plane" 29 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 30 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["PLANE_X"]: 31 | type_string = "x-plane" 32 | coeff_string = str(SurfaceCard.surface_coefficients[3]) 33 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["PLANE_Y"]: 34 | type_string = "y-plane" 35 | coeff_string = str(SurfaceCard.surface_coefficients[3]) 36 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["PLANE_Z"]: 37 | type_string = "z-plane" 38 | coeff_string = str(SurfaceCard.surface_coefficients[3]) 39 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["CYLINDER_X"]: 40 | type_string = "x-cylinder" 41 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 42 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["CYLINDER_Y"]: 43 | type_string = "y-cylinder" 44 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 45 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["CYLINDER_Z"]: 46 | type_string = "z-cylinder" 47 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 48 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["SPHERE_GENERAL"]: 49 | type_string = "sphere" 50 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 51 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["GENERAL_QUADRATIC"]: 52 | type_string = "quadric" 53 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 54 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["CONE_X"]: 55 | type_string = "x-cone" 56 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients[0:4]) 57 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["CONE_Y"]: 58 | type_string = "y-cone" 59 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients[0:4]) 60 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["CONE_Z"]: 61 | type_string = "z-cone" 62 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients[0:4]) 63 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["TORUS_X"]: 64 | type_string = "x-torus" 65 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 66 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["TORUS_Y"]: 67 | type_string = "y-torus" 68 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 69 | elif SurfaceCard.surface_type == SurfaceCard.SurfaceType["TORUS_Z"]: 70 | type_string = "z-torus" 71 | coeff_string = " ".join(str(e) for e in SurfaceCard.surface_coefficients) 72 | else: 73 | type_string = "error" 74 | coeff_string = "error" 75 | return (type_string, coeff_string) 76 | 77 | 78 | # write the surface element corresponding to the 79 | # geometry 80 | def write_openmc_surface(SurfaceCard, geometry_tree): 81 | id = SurfaceCard.surface_id 82 | type, coeffs = openmc_surface_info(SurfaceCard) 83 | ET.SubElement( 84 | geometry_tree, 85 | "surface", 86 | id=str(id), 87 | type=str(type), 88 | coeffs=str(coeffs), 89 | boundary=boundary_condition(SurfaceCard.boundary_condition), 90 | ) 91 | 92 | 93 | class OpenMCSurfaceCard(SurfaceCard): 94 | """Class to handle the creation and translation of 95 | OpenMC surface definitions 96 | """ 97 | 98 | # constructor 99 | def __init__(self, card_string): 100 | SurfaceCard.__init__(card_string) 101 | -------------------------------------------------------------------------------- /csg2csg/ParticleNames.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from enum import Enum 4 | 5 | """ Class for the storage of the generic particle names 6 | for translation of metadata downstream 7 | """ 8 | 9 | 10 | class ParticleNames(Enum): 11 | NEUTRON = 0 12 | PHOTON = 1 13 | ELECTRON = 2 14 | POSITRON = 3 15 | PROTON = 4 16 | DEUTERON = 5 17 | TRITON = 6 18 | ALPHA = 7 19 | PION_PLUS = 8 20 | PION_NEUT = 9 21 | PION_NEG = 10 22 | HELION = 11 23 | MUON_NEG = 12 24 | 25 | 26 | def particleToGeneric(particle_name): 27 | if particle_name == ParticleNames["NEUTRON"]: 28 | return "Neutron" 29 | if particle_name == ParticleNames["PHOTON"]: 30 | return "Photon" 31 | if particle_name == ParticleNames["ELECTRON"]: 32 | return "Electron" 33 | if particle_name == ParticleNames["POSITRON"]: 34 | return "Positron" 35 | if particle_name == ParticleNames["PROTON"]: 36 | return "Proton" 37 | if particle_name == ParticleNames["DEUTERON"]: 38 | return "Deuteron" 39 | if particle_name == ParticleNames["TRITON"]: 40 | return "Triton" 41 | if particle_name == ParticleNames["ALPHA"]: 42 | return "Alpha" 43 | if particle_name == ParticleNames["PION_PLUS"]: 44 | return "Positive Pion" 45 | if particle_name == ParticleNames["PION_NEUT"]: 46 | return "Neutral Pion" 47 | if particle_name == ParticleNames["PION_NEG"]: 48 | return "Negative Pion" 49 | if particle_name == ParticleNames["HELION"]: 50 | return "Helion" 51 | if particle_name == ParticleNames["MUON_NEG"]: 52 | return "Negative Muon" 53 | -------------------------------------------------------------------------------- /csg2csg/PhitsInput.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | from csg2csg.Input import InputDeck 4 | from csg2csg.MCNPSurfaceCard import MCNPSurfaceCard, write_mcnp_surface 5 | from csg2csg.MCNPCellCard import MCNPCellCard, write_mcnp_cell 6 | from csg2csg.MCNPMaterialCard import MCNPMaterialCard, write_mcnp_material 7 | 8 | 9 | class PhitsInput(InputDeck): 10 | """PhitsInput class - does the actual processing""" 11 | 12 | # constructor 13 | def __init__(self, filename=""): 14 | InputDeck.__init__(self, filename) 15 | 16 | # write the header information 17 | def __write_header_information(self, filestream): 18 | string = "[ T I T L E ]\n" 19 | string += "This PHITS file was produced automatically by csg2csg \n" 20 | string += "\n" 21 | string += "[ P A R A M E T E R S ]\n" 22 | string += "icntl = 11\n" 23 | string += "maxbch = 10\n" 24 | string += "maxcas = 1000000\n" 25 | filestream.write(string) 26 | return 27 | 28 | # Write the Phits Cell definitions 29 | def __write_phits_cells(self, filestream): 30 | filestream.write("[ C E L L ]\n") 31 | for cell in self.cell_list: 32 | write_mcnp_cell(filestream, cell, False) 33 | return 34 | 35 | # write the serpent surface definitions 36 | def __write_phits_surfaces(self, filestream): 37 | filestream.write("[ S U R F A C E ]\n") 38 | for surface in self.surface_list: 39 | write_mcnp_surface(filestream, surface) 40 | return 41 | 42 | # write the material compositions 43 | def __write_phits_importances(self, filestream): 44 | filestream.write("[ I M P O R T A N C E ]\n") 45 | filestream.write(" reg imp \n") 46 | for cell in self.cell_list: 47 | filestream.write( 48 | " " 49 | + str(cell.cell_id) 50 | + " " 51 | + str(cell.cell_importance) 52 | + "\n" 53 | ) 54 | return 55 | 56 | # write the material compositions 57 | def __write_phits_materials(self, filestream): 58 | filestream.write("[ M A T E R I A L ]\n") 59 | for material in self.material_list: 60 | write_mcnp_material(filestream, self.material_list[material], True, False) 61 | return 62 | 63 | # main write serpent method, depending upon where the geometry 64 | # came from 65 | def write_phits(self, filename, flat=True): 66 | f = open(filename, "w") 67 | self.__write_header_information(f) 68 | f.write("\n") 69 | self.__write_phits_surfaces(f) 70 | f.write("\n") 71 | self.__write_phits_cells(f) 72 | f.write("\n") 73 | self.__write_phits_importances(f) 74 | f.write("\n") 75 | self.__write_phits_materials(f) 76 | f.write("\n") 77 | f.close() 78 | -------------------------------------------------------------------------------- /csg2csg/SCONECellCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.MCNPFormatter import mcnp_line_formatter 4 | 5 | from csg2csg.CellCard import CellCard 6 | from enum import Enum 7 | import re 8 | import math 9 | 10 | # turn the generic operation type into a scone relevant text string 11 | def scone_op_from_generic(Operation): 12 | # if we are not of type operator - we are string do nowt 13 | if not isinstance(Operation, CellCard.OperationType): 14 | if Operation == "(": 15 | return " < " 16 | elif Operation == ")": 17 | return " > " 18 | else: 19 | return Operation 20 | else: 21 | # otherwise we need to do something 22 | if Operation == CellCard.OperationType["NOT"]: 23 | string = " # " 24 | elif Operation == CellCard.OperationType["AND"]: 25 | string = " " 26 | elif Operation == CellCard.OperationType["UNION"]: 27 | string = " : " 28 | else: 29 | string = "unknown operation" 30 | # return the operation 31 | return string 32 | 33 | 34 | # write the cell card for a scone cell given a generic cell card 35 | def write_scone_cell(filestream, CellCard): 36 | 37 | # In root universe, identify ....... 38 | # Presently assumes only two cells in the root, with one surface, 39 | # as in Serpent. 40 | # Allow multiple cells in root. Fix this later! 41 | #if (CellCard.cell_universe == 0 and 42 | #CellCard.cell_surface_list[0] >= 0): 43 | # return 44 | 45 | # print (CellCard) 46 | string = str(CellCard.cell_id) + " { type unionCell; id " 47 | string += str(CellCard.cell_id) + "; " 48 | 49 | # Need to keep track of universe definitions and return 50 | # to write these separately 51 | if CellCard.cell_fill != 0: 52 | string += " filltype uni; universe " + str(int(CellCard.cell_fill) + 1) + "; " 53 | 54 | # Doesn't have a universe - has a material 55 | # Due to SCONE weirdness, all materials are preceded with an 'm' 56 | if CellCard.cell_fill == 0: 57 | # material 0 is void 58 | string += " filltype mat; material " 59 | if CellCard.cell_material_number == 0: 60 | string += " void; " 61 | else: 62 | string += "m" + str(CellCard.cell_material_number) + "; " 63 | 64 | string += "surfaces [ " 65 | 66 | # build the cell description 67 | for item in CellCard.cell_interpreted: 68 | string += scone_op_from_generic(item) 69 | 70 | string += " ]; }\n" 71 | 72 | # removes any multiple spaces 73 | string = re.sub(" +", " ", string) 74 | 75 | string = mcnp_line_formatter(string) 76 | 77 | filestream.write(string) 78 | 79 | -------------------------------------------------------------------------------- /csg2csg/SCONEInput.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | from csg2csg.Input import InputDeck 4 | from csg2csg.SCONESurfaceCard import write_scone_surface 5 | from csg2csg.SCONECellCard import write_scone_cell 6 | from csg2csg.SCONEMaterialCard import write_scone_material 7 | from csg2csg.SCONEUniverseCard import write_scone_universe 8 | 9 | import logging 10 | import re 11 | 12 | 13 | class SCONEInput(InputDeck): 14 | """SCONEInput class - does the actual processing""" 15 | 16 | # constructor 17 | def __init__(self, filename=""): 18 | InputDeck.__init__(self, filename) 19 | 20 | # open the geometry card 21 | def __write_scone_geometry_start(self, filestream): 22 | filestream.write("geometry { \n") 23 | filestream.write("type geometryStd; \n") 24 | # Surely there is a way to customise this? Am I missing a 25 | # method somewhere? For now I will leave as vacuum - should be easy 26 | # for the user to change 27 | filestream.write("boundary (0 0 0 0 0 0); \n") 28 | filestream.write("graph {type shrunk;} \n") 29 | return 30 | 31 | # close the geometry card 32 | def __write_scone_geometry_end(self, filestream): 33 | filestream.write("} \n") 34 | return 35 | 36 | # open the nuclear data card 37 | def __write_scone_data_start(self, filestream): 38 | filestream.write("nuclearData { \n") 39 | filestream.write("handles { \n") 40 | filestream.write("ce {type aceNeutronDatabase; ") 41 | filestream.write("aceLibrary $SCONE_ACE; ures 0;} \n") 42 | filestream.write("} \n") 43 | return 44 | 45 | # close the data card 46 | def __write_scone_data_end(self, filestream): 47 | filestream.write("} \n") 48 | return 49 | 50 | # write the SCONE surface definitions 51 | def __write_scone_surfaces(self, filestream): 52 | filestream.write("! --- surface definitions --- !\n") 53 | filestream.write("surfaces { \n") 54 | for surface in self.surface_list: 55 | write_scone_surface(filestream, surface) 56 | filestream.write("} \n") 57 | return 58 | 59 | # Write the SCONE Cell definitions 60 | def __write_scone_cells(self, filestream): 61 | filestream.write("! --- cell definitions --- !\n") 62 | filestream.write("cells { \n") 63 | for cell in self.cell_list: 64 | write_scone_cell(filestream, cell) 65 | filestream.write("} \n") 66 | return 67 | 68 | # Write the SCONE universe definitions 69 | # Most codes have universes implicit in their cells, complicating things 70 | # slightly. 71 | def __write_scone_universes(self, filestream): 72 | filestream.write("! --- universe definitions --- !\n") 73 | filestream.write("universes { \n") 74 | for universe in self.universe_list: 75 | write_scone_universe(filestream, universe) 76 | filestream.write("} \n") 77 | 78 | # write the material compositions 79 | def __write_scone_materials(self, filestream): 80 | filestream.write("! --- material definitions --- !\n") 81 | filestream.write("materials { \n") 82 | for material in self.material_list: 83 | write_scone_material(filestream, self.material_list[material]) 84 | filestream.write("} \n") 85 | return 86 | 87 | 88 | # main write scone method, depending upon where the geometry 89 | # came from 90 | def write_scone(self, filename, flat=True): 91 | f = open(filename, "w") 92 | self.__write_scone_geometry_start(f) 93 | self.__write_scone_surfaces(f) 94 | self.__write_scone_cells(f) 95 | self.create_universes_from_cells() 96 | self.__write_scone_universes(f) 97 | self.__write_scone_geometry_end(f) 98 | self.__write_scone_data_start(f) 99 | self.__write_scone_materials(f) 100 | self.__write_scone_data_end(f) 101 | f.close() 102 | -------------------------------------------------------------------------------- /csg2csg/SCONEMaterialCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.MaterialCard import MaterialCard 4 | from csg2csg.MCNPFormatter import get_fortran_formatted_number 5 | 6 | # write a specific scone material card 7 | def write_scone_material(filestream, MaterialCard): 8 | 9 | string = "! " + MaterialCard.material_name + " \n" 10 | string += "m" + str(MaterialCard.material_number) + " { \n" 11 | string += "composition { \n" 12 | # Stick on .03 regardless until something to read temperature 13 | # from MCNP is added! 14 | # Multiply by adens because SCONE requires absolute densities 15 | dens = MaterialCard.density 16 | if dens > 0: 17 | adens = dens 18 | else: 19 | molar_mass = 0 20 | for nuc in MaterialCard.composition_dictionary: 21 | molar_mass += MaterialCard.composition_dictionary[nuc] * (int(nuc) % 1000) 22 | 23 | adens = -dens * 6.02214e-01 / molar_mass 24 | 25 | for nuc in MaterialCard.composition_dictionary: 26 | string += "{}.03 {:e}; \n".format( 27 | nuc, MaterialCard.composition_dictionary[nuc] * adens 28 | ) 29 | 30 | string += "} \n" 31 | 32 | # set the relevant colour 33 | if MaterialCard.material_colour: 34 | string += "rgb (" + MaterialCard.material_colour + "); \n" 35 | 36 | string += "} \n" 37 | filestream.write(string) 38 | return 39 | -------------------------------------------------------------------------------- /csg2csg/SCONESurfaceCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.SurfaceCard import SurfaceCard 4 | from math import sqrt, atan, degrees 5 | 6 | # write the general form of a plane 7 | def scone_plane_string(SurfaceCard): 8 | string = " plane; coeffs ( " 9 | string += str(SurfaceCard.surface_coefficients[0]) + " " 10 | string += str(SurfaceCard.surface_coefficients[1]) + " " 11 | string += str(SurfaceCard.surface_coefficients[2]) + " " 12 | string += str(SurfaceCard.surface_coefficients[3]) + "); } \n" 13 | return string 14 | 15 | 16 | # write the specific x form of the plane 17 | def scone_plane_x_string(SurfaceCard): 18 | string = " xPlane; x0 " + str(SurfaceCard.surface_coefficients[3]) 19 | string += "; } \n" 20 | return string 21 | 22 | 23 | # write the specific y form of the plane 24 | def scone_plane_y_string(SurfaceCard): 25 | string = " yPlane; y0 " + str(SurfaceCard.surface_coefficients[3]) 26 | string += "; } \n" 27 | return string 28 | 29 | 30 | # write the specific z form of the plane 31 | def scone_plane_z_string(SurfaceCard): 32 | string = " zPlane; z0 " + str(SurfaceCard.surface_coefficients[3]) 33 | string += "; } \n" 34 | return string 35 | 36 | 37 | # write a cylinder_x 38 | def scone_cylinder_x(SurfaceCard): 39 | string = " xCylinder; radius " + str(SurfaceCard.surface_coefficients[2]) 40 | string += "; origin ( 0.0 " + str(SurfaceCard.surface_coefficients[0]) 41 | string += " " + str(SurfaceCard.surface_coefficients[1]) + "); } \n" 42 | return string 43 | 44 | 45 | # write a cylinder_y 46 | def scone_cylinder_y(SurfaceCard): 47 | string = " yCylinder; radius " + str(SurfaceCard.surface_coefficients[2]) 48 | string += "; origin (" + str(SurfaceCard.surface_coefficients[0]) + " 0 " 49 | string += str(SurfaceCard.surface_coefficients[1]) + "); } \n" 50 | return string 51 | 52 | 53 | # write a cylinder_z 54 | def scone_cylinder_z(SurfaceCard): 55 | string = " zCylinder; radius " + str(SurfaceCard.surface_coefficients[2]) 56 | string += "; origin (" + str(SurfaceCard.surface_coefficients[0]) + " " 57 | string += str(SurfaceCard.surface_coefficients[1]) + " 0); } \n" 58 | return string 59 | 60 | 61 | # write a sphere 62 | def scone_sphere(SurfaceCard): 63 | string = " sphere; origin (" + str(SurfaceCard.surface_coefficients[0]) + " " 64 | string += str(SurfaceCard.surface_coefficients[1]) + " " 65 | string += str(SurfaceCard.surface_coefficients[2]) + "); radius " 66 | string += str(SurfaceCard.surface_coefficients[3]) + "; } \n" 67 | return string 68 | 69 | 70 | # write a general quadratic 71 | def scone_gq(SurfaceCard): 72 | string = " quadric; coeffs ( " 73 | for coefficient in SurfaceCard.surface_coefficients: 74 | string += " " + str(coefficient) + " " 75 | string += "); } \n" 76 | return string 77 | 78 | 79 | def scone_cone_x(SurfaceCard): 80 | x = SurfaceCard.surface_coefficients[0] 81 | y = SurfaceCard.surface_coefficients[1] 82 | z = SurfaceCard.surface_coefficients[2] 83 | t2 = SurfaceCard.surface_coefficients[3] 84 | 85 | if (len(SurfaceCard.surface_coefficients) < 5): 86 | sign = 0 87 | else: 88 | sign = SurfaceCard.surface_coefficients[4] 89 | 90 | # Do trigonometry to convert mcnp tangent squared, t2, into 91 | # angle and determine hMin and hMax. 92 | # If sign is negative, hMax = vertex = 0, hMin = -10E10 93 | # If sign is positive, hMin = vertex = 0, hMax = 10E10 94 | # If +/- 1 is not present in the MCNP definition, make cone infinite 95 | # A bit of a fudge for now. 96 | # Assume no truncation, like in MCNP 97 | if sign < 0: 98 | hMin = -1E10 99 | hMax = 0 100 | elif sign > 0: 101 | hMin = 0 102 | hMax = 1E10 103 | else: 104 | hMin = -1E10 105 | hMax = 1E10 106 | 107 | angle = degrees(atan(sqrt(t2))) 108 | 109 | string = " {} {:f} {:f} {:f} {}\n".format( 110 | " xCone; vertex (", x, y, z," ); ") 111 | string += "angle " + str(angle) + "; hMin " + str(hMin) 112 | string += "; hMax " + str(hMax) + "; } \n" 113 | 114 | return string 115 | 116 | 117 | # scone a cone along y 118 | def scone_cone_y(SurfaceCard): 119 | x = SurfaceCard.surface_coefficients[0] 120 | y = SurfaceCard.surface_coefficients[1] 121 | z = SurfaceCard.surface_coefficients[2] 122 | t2 = SurfaceCard.surface_coefficients[3] 123 | 124 | if (len(SurfaceCard.surface_coefficients) < 5): 125 | sign = 0 126 | else: 127 | sign = SurfaceCard.surface_coefficients[4] 128 | 129 | # Do trigonometry to convert mcnp tangent squared, t2, into 130 | # angle and determine hMin and hMax. 131 | # If sign is negative, hMax = vertex = 0, hMin = -10E10 132 | # If sign is positive, hMin = vertex = 0, hMax = 10E10 133 | # If +/- 1 is not present in the MCNP definition, make cone infinite 134 | # A bit of a fudge for now. 135 | # Assume no truncation, like in MCNP 136 | if sign < 0: 137 | hMin = -1E10 138 | hMax = 0 139 | elif sign > 0: 140 | hMin = 0 141 | hMax = 1E10 142 | else: 143 | hMin = -1E10 144 | hMax = 1E10 145 | 146 | angle = degrees(atan(sqrt(t2))) 147 | 148 | string = " {} {:f} {:f} {:f} {}\n".format( 149 | " yCone; vertex (", x, y, z," ); ") 150 | string += "angle " + str(angle) + "; hMin " + str(hMin) 151 | string += "; hMax " + str(hMax) + "; } \n" 152 | 153 | return string 154 | 155 | 156 | # scone a cone along z 157 | def scone_cone_z(SurfaceCard): 158 | x = SurfaceCard.surface_coefficients[0] 159 | y = SurfaceCard.surface_coefficients[1] 160 | z = SurfaceCard.surface_coefficients[2] 161 | t2 = SurfaceCard.surface_coefficients[3] 162 | 163 | if (len(SurfaceCard.surface_coefficients) < 5): 164 | sign = 0 165 | else: 166 | sign = SurfaceCard.surface_coefficients[4] 167 | 168 | # Do trigonometry to convert mcnp tangent squared, t2, into 169 | # angle and determine hMin and hMax. 170 | # If sign is negative, hMax = vertex = 0, hMin = -10E10 171 | # If sign is positive, hMin = vertex = 0, hMax = 10E10 172 | # If +/- 1 is not present in the MCNP definition, make cone infinite 173 | # A bit of a fudge for now. 174 | # Assume no truncation, like in MCNP 175 | if sign < 0: 176 | hMin = -1E10 177 | hMax = 0 178 | elif sign > 0: 179 | hMin = 0 180 | hMax = 1E10 181 | else: 182 | hMin = -1E10 183 | hMax = 1E10 184 | 185 | angle = degrees(atan(sqrt(t2))) 186 | 187 | string = " {} {:f} {:f} {:f} {}\n".format( 188 | " zCone; vertex (", x, y, z," ); ") 189 | string += "angle " + str(angle) + "; hMin " + str(hMin) 190 | string += "; hMax " + str(hMax) + "; } \n" 191 | 192 | return string 193 | 194 | 195 | """ 196 | # write a conex 197 | def serpent_cone_x(SurfaceCard): 198 | 199 | mcnp xyz r2 -1 +1 200 | * 201 | || 202 | | \ 203 | | \ 204 | | \ 205 | | \ 206 | | \ 207 | *------* 208 | 209 | From the bounding coodinate appropriate in 210 | this case - if pointing down need the lowest value 211 | 212 | 213 | # cone points down from xyz 214 | if SurfaceCard.surface_coefficients[4] == -1: 215 | h = abs(SurfaceCard.b_box[0]) 216 | x = SurfaceCard.b_box[0] 217 | # cone point up from xyz 218 | if SurfaceCard.surface_coefficients[4] == 1: 219 | h = abs(SurfaceCard.b_box[1]) 220 | x = SurfaceCard.b_box[1] 221 | 222 | y = SurfaceCard.surface_coefficients[1] 223 | z = SurfaceCard.surface_coefficients[2] 224 | r = h*sqrt(SurfaceCard.surface_coefficients[3]) 225 | 226 | string = ' {} {:f} {:f} {:f} {:f} {:f}'.format("conx",x,y,z,r,h) 227 | 228 | return string 229 | 230 | # write a cone y 231 | def serpent_cone_y(SurfaceCard): 232 | 233 | mcnp xyz r2 -1 +1 234 | * 235 | || 236 | | \ 237 | | \ 238 | | \ 239 | | \ 240 | | \ 241 | *------* 242 | 243 | From the bounding coodinate appropriate in 244 | this case - if pointing down need the lowest value 245 | 246 | 247 | # cone points down from xyz 248 | if SurfaceCard.surface_coefficients[4] == -1: 249 | h = abs(SurfaceCard.b_box[2]) 250 | y = SurfaceCard.b_box[2] 251 | # cone point up from xyz 252 | if SurfaceCard.surface_coefficients[4] == 1: 253 | h = abs(SurfaceCard.b_box[3]) 254 | y = SurfaceCard.b_box[3] 255 | 256 | x = SurfaceCard.surface_coefficients[0] 257 | z = SurfaceCard.surface_coefficients[2] 258 | r = h*sqrt(SurfaceCard.surface_coefficients[3]) 259 | 260 | string = ' {} {:f} {:f} {:f} {:f} {:f}'.format("cony",x,y,z,r,h) 261 | 262 | return string 263 | 264 | # write a cone z 265 | def serpent_cone_z(SurfaceCard): 266 | 267 | mcnp xyz r2 -1 +1 268 | * 269 | || 270 | | \ 271 | | \ 272 | | \ 273 | | \ 274 | | \ 275 | *------* 276 | 277 | From the bounding coodinate appropriate in 278 | this case - if pointing down need the lowest value 279 | 280 | 281 | # cone points down from xyz 282 | if SurfaceCard.surface_coefficients[4] == -1: 283 | h = abs(SurfaceCard.b_box[5]) 284 | z = SurfaceCard.b_box[5] 285 | # cone point up from xyz 286 | if SurfaceCard.surface_coefficients[4] == 1: 287 | h = abs(SurfaceCard.b_box[6]) 288 | z = SurfaceCard.b_box[6] 289 | 290 | x = SurfaceCard.surface_coefficients[0] 291 | y = SurfaceCard.surface_coefficients[1] 292 | r = h*sqrt(SurfaceCard.surface_coefficients[3]) 293 | 294 | 295 | return string 296 | 297 | """ 298 | 299 | # write the surface description to file 300 | def write_scone_surface(filestream, SurfaceCard): 301 | 302 | string = str(SurfaceCard.surface_id) + " { id " 303 | string += str(SurfaceCard.surface_id) + "; type " 304 | 305 | if SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_GENERAL"]: 306 | string += scone_plane_string(SurfaceCard) 307 | filestream.write(string) 308 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_X"]: 309 | string += scone_plane_x_string(SurfaceCard) 310 | filestream.write(string) 311 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_Y"]: 312 | string += scone_plane_y_string(SurfaceCard) 313 | filestream.write(string) 314 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_Z"]: 315 | string += scone_plane_z_string(SurfaceCard) 316 | filestream.write(string) 317 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_X"]: 318 | string += scone_cylinder_x(SurfaceCard) 319 | filestream.write(string) 320 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_Y"]: 321 | string += scone_cylinder_y(SurfaceCard) 322 | filestream.write(string) 323 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_Z"]: 324 | string += scone_cylinder_z(SurfaceCard) 325 | filestream.write(string) 326 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["SPHERE_GENERAL"]: 327 | string += scone_sphere(SurfaceCard) 328 | filestream.write(string) 329 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_X"]: 330 | string += scone_cone_x(SurfaceCard) 331 | filestream.write(string) 332 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_Y"]: 333 | string += scone_cone_y(SurfaceCard) 334 | filestream.write(string) 335 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_Z"]: 336 | string += scone_cone_z(SurfaceCard) 337 | filestream.write(string) 338 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["GENERAL_QUADRATIC"]: 339 | string += scone_gq(SurfaceCard) 340 | filestream.write(string) 341 | else: 342 | filestream.write("surface not supported\n") 343 | 344 | return 345 | 346 | -------------------------------------------------------------------------------- /csg2csg/SCONEUniverseCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.MCNPFormatter import mcnp_line_formatter 4 | 5 | from csg2csg.UniverseCard import UniverseCard, uni_fill, mat_fill 6 | import re 7 | import math 8 | 9 | # write the universe card for a scone universe given a generic cell card 10 | def write_scone_universe(filestream, UniverseCard): 11 | 12 | # print (UniverseCard) 13 | # TODO support lattice universes 14 | if UniverseCard.is_root: 15 | string = "root { type " 16 | string += "rootUniverse; " 17 | string += "border " + str(UniverseCard.border_surface) + "; " 18 | if UniverseCard.fill_type == uni_fill: 19 | string += "fill u<" + str(UniverseCard.fill_id + 1) + ">; " 20 | if UniverseCard.fill_type == mat_fill: 21 | # Name preceded by an m due to SCONE weirdness 22 | string += "fill m" + str(UniverseCard.fill_material) + "; " 23 | else: 24 | string = str(UniverseCard.universe_id + 1) + " { type " 25 | string += "cellUniverse; cells (" 26 | for cell_id in UniverseCard.cell_list: 27 | string += str(cell_id) + " " 28 | string += "); " 29 | 30 | # Need to increment by 1 due to SCONE not allowing Universe ID = 0 31 | # This is used by other codes to denote the base/root universe 32 | string += "id " + str(UniverseCard.universe_id + 1) +"; " 33 | 34 | # Include transformations 35 | if UniverseCard.universe_offset != 0: 36 | # Convert origin to a translation 37 | string += "translation (" 38 | for i in range(3): 39 | string += str(UniverseCard.universe_offset[i]) +" " 40 | string += "); " 41 | 42 | if UniverseCard.universe_rotation != 0: 43 | # Convert rotation matrix to Euler angles following ZXZ convention 44 | # Need to double check conventions: rotate[5] might have a negative 45 | # sign and rot[2] might be * -1 46 | rotate = UniverseCard.universe_rotation 47 | rot[0] = math.atan2(rotate[2],-rotate[5]) 48 | rot[1] = math.acos(rotate[8]) 49 | rot[2] = math.atan2(rotate[6],rotate[7]) 50 | rot = math.degrees(rot) 51 | string += "rotation (" 52 | for i in range(3): 53 | string += str(rot[i]) +" " 54 | string += "); " 55 | string += " } \n" 56 | 57 | # removes any multiple spaces 58 | string = re.sub(" +", " ", string) 59 | string = mcnp_line_formatter(string) 60 | filestream.write(string) 61 | 62 | 63 | -------------------------------------------------------------------------------- /csg2csg/SerpentCellCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.MCNPFormatter import mcnp_line_formatter 4 | 5 | from csg2csg.CellCard import CellCard 6 | from enum import Enum 7 | import re 8 | import math 9 | 10 | # turn the generic operation type into a serpent relevant text string 11 | def serpent_op_from_generic(Operation): 12 | # if we are not of type operator - we are string do nowt 13 | if not isinstance(Operation, CellCard.OperationType): 14 | if Operation == "(": 15 | return " " + Operation + " " 16 | elif Operation == ")": 17 | return " " + Operation + " " 18 | else: 19 | return Operation 20 | else: 21 | # otherwise we need to do something 22 | if Operation == CellCard.OperationType["NOT"]: 23 | string = " #" 24 | elif Operation == CellCard.OperationType["AND"]: 25 | string = " " 26 | elif Operation == CellCard.OperationType["UNION"]: 27 | string = ":" 28 | else: 29 | string = "unknown operation" 30 | # return the operation 31 | return string 32 | 33 | 34 | # write the cell card for a serpent cell given a generic cell card 35 | def write_serpent_cell(filestream, CellCard): 36 | 37 | # print (CellCard) 38 | string = "cell " + str(CellCard.cell_id) 39 | string += " " + str(CellCard.cell_universe) + " " 40 | if CellCard.cell_fill != 0: 41 | string += " fill " + str(CellCard.cell_fill) + " " 42 | 43 | # cant base level universe cant have material 44 | if CellCard.cell_fill == 0: 45 | # material 0 is void 46 | if CellCard.cell_material_number == 0: 47 | string += "void " 48 | else: 49 | string += str(CellCard.cell_material_number) + " " 50 | 51 | string += "( " 52 | 53 | # build the cell description 54 | for item in CellCard.cell_interpreted: 55 | string += serpent_op_from_generic(item) 56 | 57 | string += " ) " 58 | string += "\n" 59 | 60 | # removes any multiple spaces 61 | string = re.sub(" +", " ", string) 62 | 63 | string = mcnp_line_formatter(string) 64 | 65 | filestream.write(string) 66 | 67 | # write the universe transform 68 | if CellCard.cell_fill != 0: 69 | string = "" 70 | if CellCard.cell_universe_offset != 0 or CellCard.cell_universe_rotation != 0: 71 | string = "trans f " + str(CellCard.cell_id) + " " 72 | 73 | # universe may have no traslation? 74 | if CellCard.cell_universe_offset != 0: 75 | for i in range(3): 76 | string += str(CellCard.cell_universe_offset[i]) + " " 77 | else: 78 | string += " 0 0 0 " 79 | 80 | if CellCard.cell_universe_rotation != 0: 81 | for i in range(9): 82 | value = float(CellCard.cell_universe_rotation[i]) 83 | # transofmr should be in radians 84 | # value = math.cos(value/180.*math.pi) 85 | string += str(value) + " " 86 | else: 87 | string += "1 0 0 0 1 0 0 0 1 " 88 | string += "1 \n" 89 | 90 | filestream.write(string) 91 | 92 | 93 | class SerpentCellCard(CellCard): 94 | def __init__(self, card_string): 95 | CellCard.__init__(self, card_string) 96 | -------------------------------------------------------------------------------- /csg2csg/SerpentInput.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | from csg2csg.Input import InputDeck 4 | from csg2csg.SerpentSurfaceCard import SerpentSurfaceCard, write_serpent_surface 5 | from csg2csg.SerpentCellCard import SerpentCellCard, write_serpent_cell 6 | from csg2csg.SerpentMaterialCard import SerpentMaterialCard, write_serpent_material 7 | 8 | import logging 9 | import re 10 | 11 | 12 | class SerpentInput(InputDeck): 13 | """SerpentInput class - does the actual processing""" 14 | 15 | # constructor 16 | def __init__(self, filename=""): 17 | InputDeck.__init__(self, filename) 18 | 19 | # extract a material card from the start line until 20 | def __get_material_card(self, start_line, mat_num): 21 | # we already know that the start line has an mat name 22 | idx = start_line 23 | tokens = self.file_lines[idx].split() 24 | mat_name = tokens[1] 25 | # set the material name 26 | 27 | mat_density = float(tokens[2]) 28 | 29 | # build the first mat string 30 | material_string = " " 31 | idx += 1 32 | 33 | while True: 34 | # if at the end of the file 35 | if idx == len(self.file_lines): 36 | break 37 | while True: 38 | # its possible that we will have advanced to the end of the 39 | # file 40 | if idx == len(self.file_lines): 41 | break 42 | if "mat" == self.file_lines[idx].split()[0]: 43 | break 44 | line = self.file_lines[idx] 45 | if "%" in line: 46 | pos = line.find("%") 47 | line = line[:pos] 48 | material_string += line 49 | # increment the line that we are looking at 50 | idx += 1 51 | break 52 | 53 | material = SerpentMaterialCard(mat_num, mat_name, mat_density, material_string) 54 | 55 | self.material_list[material.material_number] = material 56 | 57 | return 58 | 59 | # get the material cards definitions 60 | def __get_material_cards(self): 61 | 62 | idx = 0 63 | mat_num = 1 64 | while True: 65 | if idx == len(self.file_lines): 66 | break 67 | 68 | # this crazy makes sure that we find an "mat " in the line 69 | if re.match(" *mat /*", self.file_lines[idx]): 70 | logging.debug("%s", "material found on line " + str(idx)) 71 | self.__get_material_card(idx, mat_num) 72 | mat_num += 1 73 | idx += 1 74 | return 75 | 76 | # process the serpent input deck and read into a generic datastructure 77 | # that we can translate to other formats 78 | def process(self): 79 | 80 | # clear out the comment and white space cards 81 | idx = 0 82 | while True: 83 | if idx == len(self.file_lines): 84 | break 85 | if self.file_lines[idx].isspace(): 86 | del self.file_lines[idx] 87 | elif self.file_lines[idx].strip().startswith("%"): 88 | del self.file_lines[idx] 89 | else: 90 | idx += 1 91 | 92 | if logging.getLogger().isEnabledFor(logging.DEBUG): 93 | logging.debug("%s", "Input Echo") 94 | for idx, line in enumerate(self.file_lines): 95 | logging.debug("%i %s", idx, line) 96 | 97 | self.__get_material_cards() 98 | 99 | # raise NotImplementedError('Serpent input files are not fully supported yet') 100 | 101 | return 102 | 103 | # Write the Serpent Cell definitions 104 | def __write_serpent_cells(self, filestream): 105 | filestream.write("% --- cell definitions --- %\n") 106 | for cell in self.cell_list: 107 | write_serpent_cell(filestream, cell) 108 | return 109 | 110 | # write the serpent surface definitions 111 | def __write_serpent_surfaces(self, filestream): 112 | filestream.write("% --- surface definitions --- %\n") 113 | for surface in self.surface_list: 114 | write_serpent_surface(filestream, surface) 115 | return 116 | 117 | # write the material compositions 118 | def __write_serpent_materials(self, filestream): 119 | filestream.write("% --- material definitions --- %\n") 120 | for material in self.material_list: 121 | write_serpent_material(filestream, self.material_list[material]) 122 | return 123 | 124 | # main write serpent method, depending upon where the geometry 125 | # came from 126 | def write_serpent(self, filename, flat=True): 127 | f = open(filename, "w") 128 | self.__write_serpent_surfaces(f) 129 | self.__write_serpent_cells(f) 130 | self.__write_serpent_materials(f) 131 | f.close() 132 | -------------------------------------------------------------------------------- /csg2csg/SerpentMaterialCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.MaterialCard import MaterialCard 4 | from csg2csg.MCNPFormatter import get_fortran_formatted_number 5 | 6 | # write a specific serpent material card 7 | def write_serpent_material(filestream, material): 8 | 9 | string = "% " + material.material_name + "\n" 10 | string += "mat " + str(material.material_number) + " " 11 | string += str(material.density) 12 | 13 | # if its a non tally material set the relevant colour 14 | if material.material_colour: 15 | string += " rgb " + material.material_colour + "\n" 16 | else: 17 | string += "\n" 18 | 19 | for nuc in material.composition_dictionary: 20 | string += "{} {:e} \n".format(nuc, material.composition_dictionary[nuc]) 21 | filestream.write(string) 22 | return 23 | 24 | 25 | """ Class to handle SerpentMaterialCard tranlation 26 | """ 27 | 28 | 29 | class SerpentMaterialCard(MaterialCard): 30 | def __init__(self, material_number, material_name, material_density, card_string): 31 | MaterialCard.__init__(self, material_number, card_string) 32 | self.material_name = material_name 33 | self.material_number = material_number 34 | self.density = material_density 35 | self.__process_string() 36 | 37 | # populate the Serpent Material Card 38 | def __process_string(self): 39 | # need to reset the dictionary 40 | # otherwise state seems to linger - weird 41 | self.composition_dictionary = {} 42 | 43 | mat_string = self.text_string 44 | mat_string = mat_string.replace("\n", " ") 45 | 46 | # split string 47 | tokens = mat_string.split() 48 | 49 | if len(tokens) % 2 != 0: 50 | raise Exception("Material string not correctly processed") 51 | 52 | while len(tokens) != 0: 53 | nuclide = tokens[0].split(".") 54 | nucid = nuclide[0] 55 | try: 56 | xsid = nuclide[1] 57 | except: 58 | xsid = "" 59 | frac = get_fortran_formatted_number(tokens[1]) 60 | tokens.pop(0) 61 | tokens.pop(0) 62 | self.composition_dictionary[nucid] = frac 63 | self.xsid_dictionary[nucid] = xsid 64 | return 65 | -------------------------------------------------------------------------------- /csg2csg/SerpentSurfaceCard.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | from csg2csg.SurfaceCard import SurfaceCard 4 | from math import sqrt 5 | 6 | # write the general form of a plane 7 | def serpent_plane_string(SurfaceCard): 8 | string = " plane " + str(SurfaceCard.surface_coefficients[0]) + " " 9 | string += str(SurfaceCard.surface_coefficients[1]) + " " 10 | string += str(SurfaceCard.surface_coefficients[2]) + " " 11 | string += str(SurfaceCard.surface_coefficients[3]) + "\n" 12 | return string 13 | 14 | 15 | # write the specific x form of the plane 16 | def serpent_plane_x_string(SurfaceCard): 17 | string = " px " + str(SurfaceCard.surface_coefficients[3]) + "\n" 18 | return string 19 | 20 | 21 | # write the specific y form of the plane 22 | def serpent_plane_y_string(SurfaceCard): 23 | string = " py " + str(SurfaceCard.surface_coefficients[3]) + "\n" 24 | return string 25 | 26 | 27 | # write the specific z form of the plane 28 | def serpent_plane_z_string(SurfaceCard): 29 | string = " pz " + str(SurfaceCard.surface_coefficients[3]) + "\n" 30 | return string 31 | 32 | 33 | # write a cylinder_x 34 | def serpent_cylinder_x(SurfaceCard): 35 | string = " cylx " + str(SurfaceCard.surface_coefficients[0]) + " " 36 | string += str(SurfaceCard.surface_coefficients[1]) + " " 37 | string += str(SurfaceCard.surface_coefficients[2]) + " " 38 | string += "\n" 39 | return string 40 | 41 | 42 | # write a cylinder_y 43 | def serpent_cylinder_y(SurfaceCard): 44 | string = " cyly " + str(SurfaceCard.surface_coefficients[0]) + " " 45 | string += str(SurfaceCard.surface_coefficients[1]) + " " 46 | string += str(SurfaceCard.surface_coefficients[2]) + " " 47 | string += "\n" 48 | return string 49 | 50 | 51 | # write a cylinder_z 52 | def serpent_cylinder_z(SurfaceCard): 53 | string = " cylz " + str(SurfaceCard.surface_coefficients[0]) + " " 54 | string += str(SurfaceCard.surface_coefficients[1]) + " " 55 | string += str(SurfaceCard.surface_coefficients[2]) + " " 56 | string += "\n" 57 | return string 58 | 59 | 60 | # write a sphere 61 | def serpent_sphere(SurfaceCard): 62 | string = " sph " + str(SurfaceCard.surface_coefficients[0]) + " " 63 | string += str(SurfaceCard.surface_coefficients[1]) + " " 64 | string += str(SurfaceCard.surface_coefficients[2]) + " " 65 | string += str(SurfaceCard.surface_coefficients[3]) 66 | string += "\n" 67 | return string 68 | 69 | 70 | # write a general quadratic 71 | def serpent_gq(SurfaceCard): 72 | string = " quadratic " 73 | for coefficient in SurfaceCard.surface_coefficients: 74 | string += " " + str(coefficient) + " " 75 | string += "\n" 76 | return string 77 | 78 | 79 | # its not clear how we deal with +-1 cones for serpent} 80 | # write a cone along x - jaakko has implemented a special 81 | # version for mcnp comparisons - ckx/y/z 82 | def serpent_cone_x(SurfaceCard): 83 | x = SurfaceCard.surface_coefficients[0] 84 | y = SurfaceCard.surface_coefficients[1] 85 | z = SurfaceCard.surface_coefficients[2] 86 | r = SurfaceCard.surface_coefficients[3] 87 | 88 | if len(SurfaceCard.surface_coefficients) > 4: 89 | side = SurfaceCard.surface_coefficients[4] 90 | string = " {} {:f} {:f} {:f} {:f} {:f}\n".format("ckx", x, y, z, r, side) 91 | else: 92 | string = " {} {:f} {:f} {:f} {:f}\n".format("ckx", x, y, z, r) 93 | 94 | return string 95 | 96 | 97 | # serpent a cone along y 98 | def serpent_cone_y(SurfaceCard): 99 | x = SurfaceCard.surface_coefficients[0] 100 | y = SurfaceCard.surface_coefficients[1] 101 | z = SurfaceCard.surface_coefficients[2] 102 | r = SurfaceCard.surface_coefficients[3] 103 | 104 | if len(SurfaceCard.surface_coefficients) > 4: 105 | side = SurfaceCard.surface_coefficients[4] 106 | string = " {} {:f} {:f} {:f} {:f} {:f}\n".format("cky", x, y, z, r, side) 107 | else: 108 | string = " {} {:f} {:f} {:f} {:f}\n".format("cky", x, y, z, r) 109 | 110 | return string 111 | 112 | 113 | # serpent a cone along z 114 | def serpent_cone_z(SurfaceCard): 115 | x = SurfaceCard.surface_coefficients[0] 116 | y = SurfaceCard.surface_coefficients[1] 117 | z = SurfaceCard.surface_coefficients[2] 118 | r = SurfaceCard.surface_coefficients[3] 119 | 120 | if len(SurfaceCard.surface_coefficients) > 4: 121 | side = SurfaceCard.surface_coefficients[4] 122 | string = " {} {:f} {:f} {:f} {:f} {:f}\n".format("ckz", x, y, z, r, side) 123 | else: 124 | string = " {} {:f} {:f} {:f} {:f}\n".format("ckz", x, y, z, r) 125 | 126 | return string 127 | 128 | 129 | # serpent a torus x 130 | def serpent_torus_x(SurfaceCard): 131 | x = SurfaceCard.surface_coefficients[0] 132 | y = SurfaceCard.surface_coefficients[1] 133 | z = SurfaceCard.surface_coefficients[2] 134 | a = SurfaceCard.surface_coefficients[3] 135 | b = SurfaceCard.surface_coefficients[4] 136 | c = SurfaceCard.surface_coefficients[5] 137 | string = " {} {:f} {:f} {:f} {:f} {:f} {:f}\n".format("torx", x, y, z, a, b, c) 138 | return string 139 | 140 | 141 | # serpent a torus y 142 | def serpent_torus_y(SurfaceCard): 143 | x = SurfaceCard.surface_coefficients[0] 144 | y = SurfaceCard.surface_coefficients[1] 145 | z = SurfaceCard.surface_coefficients[2] 146 | a = SurfaceCard.surface_coefficients[3] 147 | b = SurfaceCard.surface_coefficients[4] 148 | c = SurfaceCard.surface_coefficients[5] 149 | string = " {} {:f} {:f} {:f} {:f} {:f} {:f}\n".format("tory", x, y, z, a, b, c) 150 | return string 151 | 152 | 153 | # serpent a torus z 154 | def serpent_torus_z(SurfaceCard): 155 | x = SurfaceCard.surface_coefficients[0] 156 | y = SurfaceCard.surface_coefficients[1] 157 | z = SurfaceCard.surface_coefficients[2] 158 | a = SurfaceCard.surface_coefficients[3] 159 | b = SurfaceCard.surface_coefficients[4] 160 | c = SurfaceCard.surface_coefficients[5] 161 | string = " {} {:f} {:f} {:f} {:f} {:f} {:f}\n".format("torz", x, y, z, a, b, c) 162 | return string 163 | 164 | 165 | """ 166 | # write a conex 167 | def serpent_cone_x(SurfaceCard): 168 | 169 | mcnp xyz r2 -1 +1 170 | * 171 | || 172 | | \ 173 | | \ 174 | | \ 175 | | \ 176 | | \ 177 | *------* 178 | 179 | From the bounding coodinate appropriate in 180 | this case - if pointing down need the lowest value 181 | 182 | 183 | # cone points down from xyz 184 | if SurfaceCard.surface_coefficients[4] == -1: 185 | h = abs(SurfaceCard.b_box[0]) 186 | x = SurfaceCard.b_box[0] 187 | # cone point up from xyz 188 | if SurfaceCard.surface_coefficients[4] == 1: 189 | h = abs(SurfaceCard.b_box[1]) 190 | x = SurfaceCard.b_box[1] 191 | 192 | y = SurfaceCard.surface_coefficients[1] 193 | z = SurfaceCard.surface_coefficients[2] 194 | r = h*sqrt(SurfaceCard.surface_coefficients[3]) 195 | 196 | string = ' {} {:f} {:f} {:f} {:f} {:f}'.format("conx",x,y,z,r,h) 197 | 198 | return string 199 | 200 | # write a cone y 201 | def serpent_cone_y(SurfaceCard): 202 | 203 | mcnp xyz r2 -1 +1 204 | * 205 | || 206 | | \ 207 | | \ 208 | | \ 209 | | \ 210 | | \ 211 | *------* 212 | 213 | From the bounding coodinate appropriate in 214 | this case - if pointing down need the lowest value 215 | 216 | 217 | # cone points down from xyz 218 | if SurfaceCard.surface_coefficients[4] == -1: 219 | h = abs(SurfaceCard.b_box[2]) 220 | y = SurfaceCard.b_box[2] 221 | # cone point up from xyz 222 | if SurfaceCard.surface_coefficients[4] == 1: 223 | h = abs(SurfaceCard.b_box[3]) 224 | y = SurfaceCard.b_box[3] 225 | 226 | x = SurfaceCard.surface_coefficients[0] 227 | z = SurfaceCard.surface_coefficients[2] 228 | r = h*sqrt(SurfaceCard.surface_coefficients[3]) 229 | 230 | string = ' {} {:f} {:f} {:f} {:f} {:f}'.format("cony",x,y,z,r,h) 231 | 232 | return string 233 | 234 | # write a cone z 235 | def serpent_cone_z(SurfaceCard): 236 | 237 | mcnp xyz r2 -1 +1 238 | * 239 | || 240 | | \ 241 | | \ 242 | | \ 243 | | \ 244 | | \ 245 | *------* 246 | 247 | From the bounding coodinate appropriate in 248 | this case - if pointing down need the lowest value 249 | 250 | 251 | # cone points down from xyz 252 | if SurfaceCard.surface_coefficients[4] == -1: 253 | h = abs(SurfaceCard.b_box[5]) 254 | z = SurfaceCard.b_box[5] 255 | # cone point up from xyz 256 | if SurfaceCard.surface_coefficients[4] == 1: 257 | h = abs(SurfaceCard.b_box[6]) 258 | z = SurfaceCard.b_box[6] 259 | 260 | x = SurfaceCard.surface_coefficients[0] 261 | y = SurfaceCard.surface_coefficients[1] 262 | r = h*sqrt(SurfaceCard.surface_coefficients[3]) 263 | 264 | 265 | return string 266 | 267 | """ 268 | 269 | # write the surface description to file 270 | def write_serpent_surface(filestream, SurfaceCard): 271 | # NOTE this appears to be a nice way to get a pythonic case statement 272 | # is it equally ugly as below? 273 | # return { 274 | # SurfaceCard.SurfaceType["PLANE_GENERAL"]: surface_plane_write(Surface, 275 | # SurfaceCard.SurfaceType["CYLINDER_Y"]: "cylinder_y\n" 276 | # }.get(SurfaceCard.surface_type,"surface not supported") 277 | 278 | string = "surf " + str(SurfaceCard.surface_id) 279 | 280 | if SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_GENERAL"]: 281 | string += serpent_plane_string(SurfaceCard) 282 | filestream.write(string) 283 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_X"]: 284 | string += serpent_plane_x_string(SurfaceCard) 285 | filestream.write(string) 286 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_Y"]: 287 | string += serpent_plane_y_string(SurfaceCard) 288 | filestream.write(string) 289 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["PLANE_Z"]: 290 | string += serpent_plane_z_string(SurfaceCard) 291 | filestream.write(string) 292 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_X"]: 293 | string += serpent_cylinder_x(SurfaceCard) 294 | filestream.write(string) 295 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_Y"]: 296 | string += serpent_cylinder_y(SurfaceCard) 297 | filestream.write(string) 298 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CYLINDER_Z"]: 299 | string += serpent_cylinder_z(SurfaceCard) 300 | filestream.write(string) 301 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["SPHERE_GENERAL"]: 302 | string += serpent_sphere(SurfaceCard) 303 | filestream.write(string) 304 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_X"]: 305 | string += serpent_cone_x(SurfaceCard) 306 | filestream.write(string) 307 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_Y"]: 308 | string += serpent_cone_y(SurfaceCard) 309 | filestream.write(string) 310 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["CONE_Z"]: 311 | string += serpent_cone_z(SurfaceCard) 312 | filestream.write(string) 313 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["TORUS_X"]: 314 | string += serpent_torus_x(SurfaceCard) 315 | filestream.write(string) 316 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["TORUS_Y"]: 317 | string += serpent_torus_y(SurfaceCard) 318 | filestream.write(string) 319 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["TORUS_Z"]: 320 | string += serpent_torus_z(SurfaceCard) 321 | filestream.write(string) 322 | elif SurfaceCard.surface_type is SurfaceCard.SurfaceType["GENERAL_QUADRATIC"]: 323 | string += serpent_gq(SurfaceCard) 324 | filestream.write(string) 325 | else: 326 | filestream.write("surface not supported\n") 327 | return 328 | 329 | 330 | class SerpentSurfaceCard(SurfaceCard): 331 | def __init__(self, card_string): 332 | SurfaceCard.__init__(self, card_string) 333 | 334 | def write(self): 335 | print("hello") 336 | -------------------------------------------------------------------------------- /csg2csg/SurfaceCard.py: -------------------------------------------------------------------------------- 1 | from csg2csg.Card import Card 2 | from enum import Enum 3 | 4 | from copy import deepcopy 5 | 6 | 7 | class SurfaceCard(Card): 8 | """Class for the storage of the generic SurfaceCard type 9 | Methods for the generation of flat geometry surface card data 10 | should be place here. Classes needing to write flat 11 | surface card data should be implemented in its own 12 | CodeSurfaceCard.py file 13 | """ 14 | 15 | class BoundaryCondition(Enum): 16 | TRANSMISSION = 0 17 | VACUUM = 1 18 | REFLECTING = 2 19 | PERIODIC = 3 20 | WHITE = 4 21 | 22 | class SurfaceType(Enum): 23 | PLANE_GENERAL = 0 24 | PLANE_X = 1 25 | PLANE_Y = 2 26 | PLANE_Z = 3 27 | CYLINDER_X = 4 28 | CYLINDER_Y = 5 29 | CYLINDER_Z = 6 30 | SPHERE_GENERAL = 7 31 | CONE_X = 8 32 | CONE_Y = 9 33 | CONE_Z = 10 34 | TORUS_X = 11 35 | TORUS_Y = 12 36 | TORUS_Z = 13 37 | GENERAL_QUADRATIC = 14 38 | MACRO_RPP = 15 39 | MACRO_BOX = 16 40 | MACRO_RCC = 17 41 | 42 | # constructor for building a surface card 43 | def __init__(self, card_string): 44 | self.surface_type = 0 45 | self.surface_id = 0 46 | self.surface_transform = 0 47 | self.surface_coefficients = [] 48 | self.boundary_condition = self.BoundaryCondition["TRANSMISSION"] 49 | self.comment = "" 50 | self.b_box = [0, 0, 0, 0, 0, 0] # b 51 | Card.__init__(self, card_string) 52 | 53 | def __str__(self): 54 | string = "SurfaceCard: \n" 55 | string += "Surface ID " + str(self.surface_id) + "\n" 56 | string += "Transform ID " + str(self.surface_transform) + "\n" 57 | string += "Surface Type " + str(self.surface_type) + "\n" 58 | string += "Surface Coefficients " + str(self.surface_coefficients) + "\n" 59 | string += "Boundary Condition " + str(self.boundary_condition) + "\n" 60 | string += "Comment: " + str(self.comment) + "\n" 61 | return string 62 | 63 | """ 64 | Provides the diff of two surfaces 65 | 66 | Surface: An instance of a Surface Class 67 | both: check the diff of the inverse of the surface - only meaningful for plains 68 | already_generalised: surface has already been generalised 69 | 70 | Returns: Bool,Bool (surface same, inverse_same) 71 | e.g. T,F - surface was the same, but it wasnt the inverted one 72 | T,T - surface was the same, but it was the inverted one 73 | F,F - surfaces wasnt the same 74 | F,T - cannot occur 75 | """ 76 | 77 | def diff(self, surface, both=True, already_generalised=False): 78 | # generalise the surfaces 79 | if not already_generalised: 80 | self.generalise() 81 | surface.generalise() 82 | 83 | # they arent the same type 84 | if surface.surface_type is not self.surface_type: 85 | return (False, False) 86 | 87 | # they are the same 88 | if surface.surface_coefficients == self.surface_coefficients: 89 | return (True, False) 90 | 91 | # comparing the other side 92 | elif both: 93 | surface.reverse() 94 | if surface.surface_coefficients == self.surface_coefficients: 95 | surface.reverse() 96 | return (True, True) 97 | else: 98 | surface.reverse() 99 | return (False, False) 100 | 101 | # no matches 102 | else: 103 | return (False, False) 104 | 105 | """ 106 | Function to reverse the normal of the surface definition 107 | 108 | modifies the class in place 109 | """ 110 | 111 | def reverse(self): 112 | surf_coeffs = [i * -1 for i in self.surface_coefficients] 113 | self.surface_coefficients = surf_coeffs 114 | return 115 | 116 | def set_type(self, surf_id, surf_transform, surf_type, coords): 117 | self.surface_id = surf_id 118 | self.surface_transform = surf_transform 119 | self.surface_type = surf_type 120 | self.surface_coefficients = coords 121 | 122 | # test if the current surface is a macrobody or not 123 | def is_macrobody(self): 124 | if self.surface_type == self.SurfaceType["MACRO_RPP"]: 125 | return True 126 | if self.surface_type == self.SurfaceType["MACRO_BOX"]: 127 | return True 128 | if self.surface_type == self.SurfaceType["MACRO_RCC"]: 129 | return True 130 | return False 131 | 132 | # get the bounding box 133 | def bounding_box(self): 134 | # bounding box return value 135 | b_box = [0, 0, 0, 0, 0, 0] 136 | 137 | if self.surface_type == self.SurfaceType["PLANE_X"]: 138 | b_box[0] = self.surface_coefficients[3] 139 | b_box[1] = self.surface_coefficients[3] 140 | elif self.surface_type == self.SurfaceType["PLANE_Y"]: 141 | b_box[2] = self.surface_coefficients[3] 142 | b_box[3] = self.surface_coefficients[3] 143 | elif self.surface_type == self.SurfaceType["PLANE_Z"]: 144 | b_box[4] = self.surface_coefficients[3] 145 | b_box[5] = self.surface_coefficients[3] 146 | elif self.surface_type == self.SurfaceType["CYLINDER_X"]: 147 | b_box[2] = self.surface_coefficients[0] - self.surface_coefficients[2] 148 | b_box[3] = self.surface_coefficients[0] + self.surface_coefficients[2] 149 | b_box[4] = self.surface_coefficients[1] - self.surface_coefficients[2] 150 | b_box[5] = self.surface_coefficients[1] + self.surface_coefficients[2] 151 | elif self.surface_type == self.SurfaceType["CYLINDER_Y"]: 152 | b_box[0] = self.surface_coefficients[0] - self.surface_coefficients[2] 153 | b_box[1] = self.surface_coefficients[0] + self.surface_coefficients[2] 154 | b_box[4] = self.surface_coefficients[1] - self.surface_coefficients[2] 155 | b_box[5] = self.surface_coefficients[1] + self.surface_coefficients[2] 156 | elif self.surface_type == self.SurfaceType["CYLINDER_Z"]: 157 | b_box[0] = self.surface_coefficients[0] - self.surface_coefficients[2] 158 | b_box[1] = self.surface_coefficients[0] + self.surface_coefficients[2] 159 | b_box[2] = self.surface_coefficients[1] - self.surface_coefficients[2] 160 | b_box[3] = self.surface_coefficients[1] + self.surface_coefficients[2] 161 | elif self.surface_type == self.SurfaceType["SPHERE_GENERAL"]: 162 | b_box[0] = self.surface_coefficients[0] - self.surface_coefficients[3] 163 | b_box[1] = self.surface_coefficients[0] + self.surface_coefficients[3] 164 | b_box[2] = self.surface_coefficients[1] - self.surface_coefficients[3] 165 | b_box[3] = self.surface_coefficients[1] + self.surface_coefficients[3] 166 | b_box[4] = self.surface_coefficients[2] - self.surface_coefficients[3] 167 | b_box[5] = self.surface_coefficients[2] + self.surface_coefficients[3] 168 | return b_box 169 | 170 | # turn the surface into a gq type in preparation for transformation 171 | def generalise(self): 172 | a = 0 173 | b = 0 174 | c = 0 175 | d = 0 176 | e = 0 177 | f = 0 178 | g = 0 179 | h = 0 180 | j = 0 181 | k = 0 182 | 183 | # note the -ve sign is due to the special way that MCNP defines 184 | # planes (in difference to say FLUKA) 185 | if self.surface_type == self.SurfaceType["PLANE_X"]: 186 | g = 1.0 187 | k = -1.0 * self.surface_coefficients[3] 188 | elif self.surface_type == self.SurfaceType["PLANE_Y"]: 189 | h = 1.0 190 | k = -1.0 * self.surface_coefficients[3] 191 | elif self.surface_type == self.SurfaceType["PLANE_Z"]: 192 | j = 1.0 193 | k = -1.0 * self.surface_coefficients[3] 194 | elif self.surface_type == self.SurfaceType["PLANE_GENERAL"]: 195 | g = self.surface_coefficients[0] 196 | h = self.surface_coefficients[1] 197 | j = self.surface_coefficients[2] 198 | k = -1.0 * self.surface_coefficients[3] 199 | # all spheres are general 200 | elif self.surface_type == self.SurfaceType["SPHERE_GENERAL"]: 201 | a = 1 202 | b = 1 203 | c = 1 204 | d = 0 205 | e = 0 206 | f = 0 207 | g = -2 * self.surface_coefficients[0] 208 | h = -2 * self.surface_coefficients[1] 209 | j = -2 * self.surface_coefficients[2] 210 | k = ( 211 | self.surface_coefficients[0] ** 2 212 | + self.surface_coefficients[1] ** 2 213 | + self.surface_coefficients[2] ** 2 214 | - self.surface_coefficients[3] ** 2 215 | ) 216 | # todo need to check cylinder equation 217 | elif self.surface_type == self.SurfaceType["CYLINDER_X"]: 218 | b = 1 219 | c = 1 220 | h = -2 * self.surface_coefficients[0] 221 | j = -2 * self.surface_coefficients[1] 222 | k = ( 223 | self.surface_coefficients[0] ** 2 224 | + self.surface_coefficients[1] ** 2 225 | - self.surface_coefficients[2] ** 2 226 | ) 227 | elif self.surface_type == self.SurfaceType["CYLINDER_Y"]: 228 | a = 1 229 | c = 1 230 | g = -2 * self.surface_coefficients[0] 231 | j = -2 * self.surface_coefficients[1] 232 | k = ( 233 | self.surface_coefficients[0] ** 2 234 | + self.surface_coefficients[1] ** 2 235 | - self.surface_coefficients[2] ** 2 236 | ) 237 | elif self.surface_type == self.SurfaceType["CYLINDER_Z"]: 238 | a = 1 239 | b = 1 240 | g = -2 * self.surface_coefficients[0] 241 | h = -2 * self.surface_coefficients[1] 242 | k = ( 243 | self.surface_coefficients[0] ** 2 244 | + self.surface_coefficients[1] ** 2 245 | - self.surface_coefficients[2] ** 2 246 | ) 247 | # todo check cone equation 248 | elif self.surface_type == self.SurfaceType["CONE_X"]: 249 | a = -1 * self.surface_coefficients[3] 250 | b = 1 251 | c = 1 252 | g = 2 * self.surface_coefficients[0] * self.surface_coefficients[3] 253 | h = -2 * self.surface_coefficients[1] 254 | j = -2 * self.surface_coefficients[2] 255 | k = ( 256 | -self.surface_coefficients[3] * self.surface_coefficients[0] ** 2 257 | + self.surface_coefficients[1] ** 2 258 | + self.surface_coefficients[2] ** 2 259 | ) 260 | elif self.surface_type == self.SurfaceType["CONE_Y"]: 261 | a = 1 262 | b = -1 * self.surface_coefficients[3] 263 | c = 1 264 | g = -2 * self.surface_coefficients[0] 265 | h = 2 * self.surface_coefficients[1] * self.surface_coefficients[3] 266 | j = -2 * self.surface_coefficients[2] 267 | k = ( 268 | self.surface_coefficients[0] ** 2 269 | - self.surface_coefficients[3] * self.surface_coefficients[1] ** 2 270 | + self.surface_coefficients[2] ** 2 271 | ) 272 | elif self.surface_type == self.SurfaceType["CONE_Z"]: 273 | a = 1 274 | b = 1 275 | c = -1 * self.surface_coefficients[3] 276 | g = -2 * self.surface_coefficients[0] 277 | h = -2 * self.surface_coefficients[1] 278 | j = 2 * self.surface_coefficients[2] * self.surface_coefficients[3] 279 | k = ( 280 | self.surface_coefficients[0] ** 2 281 | + self.surface_coefficients[1] ** 2 282 | - self.surface_coefficients[2] ** 2 * self.surface_coefficients[3] 283 | ) 284 | elif self.surface_type == self.SurfaceType["GENERAL_QUADRATIC"]: 285 | a = self.surface_coefficients[0] 286 | b = self.surface_coefficients[1] 287 | c = self.surface_coefficients[2] 288 | d = self.surface_coefficients[3] 289 | e = self.surface_coefficients[4] 290 | f = self.surface_coefficients[5] 291 | g = self.surface_coefficients[6] 292 | h = self.surface_coefficients[7] 293 | j = self.surface_coefficients[8] 294 | k = self.surface_coefficients[9] 295 | elif self.surface_type in { 296 | self.SurfaceType["TORUS_X"], 297 | self.SurfaceType["TORUS_Y"], 298 | self.SurfaceType["TORUS_Z"], 299 | }: 300 | return 301 | else: 302 | print("could not classify surface", self.surface_id, self.surface_type) 303 | 304 | new_surface_coefficients = [0.0] * 10 305 | 306 | new_surface_coefficients[0] = a 307 | new_surface_coefficients[1] = b 308 | new_surface_coefficients[2] = c 309 | new_surface_coefficients[3] = d 310 | new_surface_coefficients[4] = e 311 | new_surface_coefficients[5] = f 312 | new_surface_coefficients[6] = g 313 | new_surface_coefficients[7] = h 314 | new_surface_coefficients[8] = j 315 | new_surface_coefficients[9] = k 316 | 317 | # dont forget to turn the type into a gq 318 | self.surface_type = self.SurfaceType["GENERAL_QUADRATIC"] 319 | self.surface_coefficients = new_surface_coefficients 320 | return 321 | 322 | def simplify(self): 323 | if self.surface_type != self.SurfaceType["GENERAL_QUADRATIC"]: 324 | return 325 | # then its a plane! 326 | if all(value == 0.0 for value in self.surface_coefficients[0:5]): 327 | self.surface_coefficients[0] = self.surface_coefficients[6] 328 | self.surface_coefficients[1] = self.surface_coefficients[7] 329 | self.surface_coefficients[2] = self.surface_coefficients[8] 330 | self.surface_coefficients[3] = -1.0 * self.surface_coefficients[9] 331 | self.surface_coefficients = self.surface_coefficients[0:4] 332 | self.surface_type = self.SurfaceType["PLANE_GENERAL"] 333 | elif all(value == 0.0 for value in self.surface_coefficients[0:7]): 334 | self.surface_coefficients[0] = 0.0 335 | self.surface_coefficients[1] = 0.0 336 | self.surface_coefficients[2] = 1.0 337 | self.surface_coefficients[3] = -1.0 * self.surface_coefficients[9] 338 | self.surface_coefficients = self.surface_coefficients[0:4] 339 | self.surface_type = self.SurfaceType["PLANE_Z"] 340 | else: 341 | return 342 | -------------------------------------------------------------------------------- /csg2csg/UniverseCard.py: -------------------------------------------------------------------------------- 1 | # /usr/env/python3 2 | 3 | from csg2csg.Card import Card 4 | from enum import Enum 5 | 6 | uni_fill = 1 7 | mat_fill = 2 8 | 9 | class UniverseCard(Card): 10 | """Class for the storage of the Generic UniverseCard type. 11 | Methods for the generation of UniverseCards should be placed 12 | here. Classes instantiating UniverseCard objects should be 13 | implemented in its own CodeUniverseCard.py file. 14 | This is primarily intended for SCONE - do any other codes 15 | have explicit cell universe cards? Maybe this is useful for 16 | translating lattices? 17 | """ 18 | 19 | # constructor for the universecard class 20 | def __init__(self): 21 | self.universe_comment = "" 22 | self.universe_id = 0 23 | self.cell_list = set() 24 | self.fill_type = 0 25 | self.fill_id = 0 26 | self.fill_mat = '' 27 | self.offset = 0 28 | self.rotation = 0 29 | self.is_root = 0 30 | self.border_surface = 0 31 | self.universe_offset = 0 32 | self.universe_rotation = 0 33 | #Card.__init__(self, card_string) 34 | 35 | # print method 36 | def __str__(self): 37 | string = "Universe Card: \n" 38 | string += "Universe ID " + str(self.universe_id) + "\n" 39 | string += "Comment " + str(self.universe_comment) + "\n" 40 | string += "Is cell universe? " + str(bool(self.cell_list)) + "\n" 41 | if bool(self.cell_list): 42 | string += "Cells in Universe " + str(self.cell_list) + "\n" 43 | string += "Is root? " + str(self.is_root) + "\n" 44 | if self.is_root: 45 | string += "Bounding surface " + str(self.border_surface) + "\n" 46 | if self.fill_type == uni_fill: 47 | string += "Contains universe " + str(self.fill_id) + "\n" 48 | else: 49 | string += "Contains material " + self.fill_mat + "\n" 50 | return string 51 | 52 | # Build from CellCard list 53 | # Loops through cell list to identify any cells it contains. 54 | # Also identifies which cells contain it and takes their 55 | # cell transformations as its own. 56 | def build_from_cell_list(self, uni_id, cell_list): 57 | self.cell_list = [] 58 | self.universe_id = uni_id 59 | 60 | #print(self.__str__()) 61 | #print(uni_id) 62 | 63 | # This is the root universe 64 | if uni_id == 0: 65 | self.is_root = 1 66 | 67 | for cell in cell_list: 68 | 69 | # Universe contains this cell 70 | if int(cell.cell_universe) == uni_id: 71 | self.cell_list.append(cell.cell_id) 72 | 73 | # Search the root universe for a cell which has a single 74 | # surface in which it is in the positive halfspace. 75 | # This is (probably??) the bounding surface. 76 | # The indicator that a cell is definitely in the positive 77 | # halfspace comes from cell_text_description. Check it for 78 | # a single entry which is positive. 79 | numeric_list = [item for item in cell.cell_text_description if item.lstrip('-').isdigit()] 80 | halfspaces = [int(x) for x in numeric_list] 81 | if (uni_id == 0 and len(halfspaces) == 1): 82 | surface_id = halfspaces[0] 83 | if surface_id > 0: 84 | self.border_surface = surface_id 85 | 86 | 87 | # Universe is contained by this cell 88 | if cell.cell_fill == uni_id: 89 | 90 | # Apply rotations and transformations as appropriate 91 | if cell.cell_universe_rotation != 0: 92 | self.universe_rotation = cell.cell_universe_rotation 93 | 94 | if cell.cell_universe_offset != 0: 95 | self.universe_offset = cell.cell_universe_offset 96 | 97 | # Make sure there isn't something silly 98 | #if (cell.cell_fill == uni_id) and (cell.cell_universe == uni_id): 99 | # print(cell.cell_id) 100 | # print(uni_id) 101 | # raise ValueError('Cell contains and is contained by the same universe') 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /csg2csg/Vector.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/python3 2 | 3 | # functions for vector manipulation to avoid brining in a numpy dependency 4 | # only ever meant to be used on single row vectors, of length 3 5 | 6 | 7 | def subtract(v1, v2): 8 | vec = [0.0] * 3 9 | vec[0] = v1[0] - v2[0] 10 | vec[1] = v1[1] - v2[1] 11 | vec[2] = v1[2] - v2[2] 12 | return vec 13 | 14 | 15 | def add(v1, v2): 16 | vec = [0.0] * 3 17 | vec[0] = v1[0] + v2[0] 18 | vec[1] = v1[1] + v2[1] 19 | vec[2] = v1[2] + v2[2] 20 | return vec 21 | 22 | 23 | def cross(v1, v2): 24 | vec = [0.0] * 3 25 | vec[0] = (v1[1] * v2[2]) - (v2[1] * v1[2]) 26 | vec[1] = (v1[0] * v2[2]) - (v2[0] * v1[2]) 27 | vec[2] = (v1[0] * v2[1]) - (v2[0] * v1[1]) 28 | return vec 29 | -------------------------------------------------------------------------------- /csg2csg/__init__.py: -------------------------------------------------------------------------------- 1 | name = "csg2csg" 2 | -------------------------------------------------------------------------------- /csg2csg/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from csg2csg.MCNPInput import MCNPInput 4 | from csg2csg.SerpentInput import SerpentInput 5 | from csg2csg.OpenMCInput import OpenMCInput 6 | from csg2csg.FLUKAInput import FLUKAInput 7 | from csg2csg.PhitsInput import PhitsInput 8 | from csg2csg.SCONEInput import SCONEInput 9 | 10 | # for debug info 11 | import logging, sys 12 | import argparse 13 | import os 14 | 15 | # make a directory in which wthe output of csg2csg will 16 | # be stored 17 | def mkdir(directory): 18 | try: 19 | os.mkdir(directory) 20 | except: 21 | pass 22 | 23 | 24 | # the main worker 25 | def main(): 26 | 27 | argv = sys.argv[1:] 28 | 29 | parser = argparse.ArgumentParser(description="csg conversion tool.") 30 | 31 | parser.add_argument( 32 | "-d", "--debug", help="Turn on debug logging", action="store_true" 33 | ) 34 | 35 | parser.add_argument("-i", "--input", help="Filename to read in", required=True) 36 | 37 | parser.add_argument( 38 | "-f", 39 | "--format", 40 | choices=["mcnp", "serpent", "openmc", "phits", "fluka", "scone"], 41 | help="format of the input file", 42 | default="mcnp", 43 | ) 44 | 45 | parser.add_argument( 46 | "-p", 47 | "--preserve", 48 | help="Preserve existing cross section id numbers on write", 49 | action="store_true", 50 | ) 51 | 52 | parser.add_argument( 53 | "-o", "--output", nargs="+", help="Output code selections", default="all" 54 | ) 55 | 56 | parser.add_argument( 57 | "-q", 58 | "--quick", 59 | help="Perform quick translation, skip surface comparison - model may not transport", 60 | action="store_true", 61 | ) 62 | 63 | # parse the arguments 64 | args = parser.parse_args(argv) 65 | 66 | # if debugging requested 67 | if args.debug: 68 | logging.basicConfig(filename="csg2csg.log", level=logging.DEBUG) 69 | 70 | logging.info("Started") 71 | 72 | filename = args.input 73 | 74 | if "all" in args.output: 75 | codes = ["mcnp", "serpent", "openmc", "phits", "fluka", "scone"] 76 | else: 77 | codes = args.output 78 | 79 | if args.format == "mcnp": 80 | # read the mcnp input 81 | input = MCNPInput(filename, args.quick) 82 | input.read() 83 | input.process() 84 | elif args.format == "serpent": 85 | # read the serpent input 86 | input = SerpentInput(filename) 87 | input.read() 88 | input.process() 89 | elif args.format == "openmc": 90 | raise NotImplementedError("OpenMC input files are not supported yet") 91 | elif args.format == "phits": 92 | raise NotImplementedError("Phits input files are not supported yet") 93 | elif args.format == "fluka": 94 | raise NotImplementedError("Fluka input files are not supported yet") 95 | elif args.format == "scone": 96 | raise NotImplementedError("SCONE input files are not supported yet") 97 | 98 | for code in codes: 99 | if "serpent" in code: 100 | print("Producing Serpent output...") 101 | serpent = SerpentInput() 102 | serpent.from_input(input) 103 | mkdir("serpent") 104 | serpent.write_serpent("serpent/file.serp") 105 | if "mcnp" in code: 106 | print("Producing MCNP output...") 107 | mcnp = MCNPInput() 108 | mcnp.from_input(input) 109 | mkdir("mcnp") 110 | mcnp.write_mcnp("mcnp/file.mcnp") 111 | if "openmc" in code: 112 | print("Producing OpenMC output...") 113 | openmc = OpenMCInput() 114 | openmc.from_input(input) 115 | mkdir("openmc") 116 | openmc.write_openmc("openmc") 117 | if "phits" in code: 118 | print("Producing Phits output...") 119 | phits = PhitsInput() 120 | phits.from_input(input) 121 | mkdir("phits") 122 | phits.write_phits("phits/phits.in") 123 | if "fluka" in code: 124 | print("Producing FLUKA output...") 125 | fluka = FLUKAInput() 126 | fluka.from_input(input) 127 | mkdir("fluka") 128 | fluka.write_fluka("fluka/fluka.inp") 129 | if "scone" in code: 130 | print("Producing SCONE output...") 131 | scone = SCONEInput() 132 | scone.from_input(input) 133 | mkdir("scone") 134 | scone.write_scone("scone/scone.inp") 135 | 136 | logging.info("Finished") 137 | 138 | 139 | if __name__ == "__main__": 140 | main() 141 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "csg2csg" 7 | version = "0.0.27" 8 | authors = [ 9 | { name="Andrew Davis", email="andrew.davis@ukaea.uk" }, 10 | ] 11 | description = "Convert CSG geometry into different formats" 12 | readme = "README.md" 13 | requires-python = ">=3.7" 14 | dependencies = [ 15 | "numpy", 16 | ] 17 | classifiers = [ 18 | "Programming Language :: Python :: 3", 19 | # "License :: OSI Approved :: MIT License", 20 | "Operating System :: OS Independent", 21 | ] 22 | 23 | [project.optional-dependencies] 24 | dev = [ 25 | "pytest", 26 | "pytest-cov", 27 | "ruff", 28 | ] 29 | 30 | [project.scripts] 31 | csg2csg = "csg2csg.__main__:main" 32 | 33 | [project.urls] 34 | "Homepage" = "https://github.com/makeclean/csg2csg" 35 | "Bug Tracker" = "https://github.com/makeclean/csg2csg/issues" 36 | 37 | [tool.setuptools] 38 | packages = ["csg2csg"] 39 | -------------------------------------------------------------------------------- /test-data/spheres.i: -------------------------------------------------------------------------------- 1 | test problem for csg2csg 2 | 1 0 -1 imp:n=1 3 | 2 0 -2 1 imp:n=1 4 | 3 0 -3 2 imp:n=1 5 | 4 0 3 imp:n=0 6 | 7 | 1 so 10 8 | 2 so 20 9 | 3 so 30 10 | 11 | print 12 | nps 1e6 13 | -------------------------------------------------------------------------------- /tests/test_lineendings.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def cleanup(): 5 | subprocess.check_call("rm -rf mcnp fluka openmc serpent file.mcnp", shell=True) 6 | 7 | 8 | def test_windows_line_endings(): 9 | 10 | subprocess.check_call( 11 | "csg2csg -i test-data/spheres.i -f mcnp -o all", shell=True 12 | ) 13 | # UNIX to DOS (adding CRs) 14 | subprocess.check_call( 15 | r"sed -e 's/$/\r/' test-data/spheres.i > spheres_win.i", 16 | shell=True, 17 | ) 18 | subprocess.check_call( 19 | "csg2csg -i spheres_win.i -f mcnp -o all", shell=True 20 | ) 21 | cleanup() 22 | 23 | 24 | # round trip the MCNP file produced from the line above - it should be 25 | # identical 26 | def test_round_trip(): 27 | cleanup() 28 | subprocess.check_call( 29 | "csg2csg -i test-data/spheres.i -f mcnp -o mcnp", shell=True 30 | ) 31 | 32 | subprocess.check_call("mv mcnp/file.mcnp . ", shell=True) 33 | subprocess.check_call("rm -rf mcnp fluka openmc serpent", shell=True) 34 | 35 | subprocess.check_call( 36 | "csg2csg -i file.mcnp -f mcnp -o mcnp", shell=True 37 | ) 38 | subprocess.check_call("diff file.mcnp mcnp/file.mcnp", shell=True) 39 | cleanup() 40 | -------------------------------------------------------------------------------- /tests/test_materialdata.py: -------------------------------------------------------------------------------- 1 | from csg2csg.MaterialData import MaterialData 2 | 3 | 4 | def test_get_nucs(): 5 | mat_data = MaterialData() 6 | nuclides = mat_data.get_nucs(1000) 7 | assert len(nuclides) == 2 8 | assert 1001 in nuclides 9 | assert 1002 in nuclides 10 | 11 | 12 | def test_get_nucs_fe(): 13 | mat_data = MaterialData() 14 | nuclides = mat_data.get_nucs(26000) 15 | assert len(nuclides) == 4 16 | assert 26054 in nuclides 17 | assert 26056 in nuclides 18 | assert 26057 in nuclides 19 | assert 26058 in nuclides 20 | 21 | 22 | def test_get_nucs_u(): 23 | mat_data = MaterialData() 24 | nuclides = mat_data.get_nucs(92000) 25 | assert len(nuclides) == 3 26 | assert 92234 in nuclides 27 | assert 92235 in nuclides 28 | assert 92238 in nuclides 29 | 30 | 31 | def test_atomic_mass_calc(): 32 | mat_data = MaterialData() 33 | atomic_mass = mat_data.atomic_mass(1000) 34 | assert atomic_mass == 1.0079407540557772 35 | 36 | atomic_mass = mat_data.atomic_mass(2000) 37 | assert atomic_mass == 4.002601932120928 38 | 39 | atomic_mass = mat_data.atomic_mass(26000) 40 | assert atomic_mass == 55.84514442998594 41 | 42 | 43 | def test_zz_zaid(): 44 | mat_data = MaterialData() 45 | zz = mat_data.get_zz(1001) 46 | assert zz == 1 47 | zz = mat_data.get_zz(26000) 48 | assert zz == 26 49 | zz = mat_data.get_zz(92000) 50 | assert zz == 92 51 | 52 | 53 | def test_aa_zaid(): 54 | mat_data = MaterialData() 55 | aa = mat_data.get_aa(1001) 56 | assert aa == 1 57 | aa = mat_data.get_aa(26000) 58 | assert aa == 0 59 | aa = mat_data.get_aa(92235) 60 | assert aa == 235 61 | -------------------------------------------------------------------------------- /tests/test_mcnpcell.py: -------------------------------------------------------------------------------- 1 | from csg2csg.MCNPCellCard import MCNPCellCard, mcnp_line_formatter 2 | 3 | 4 | # TODO definitely add some more robust testing of the position of the 5 | # logical operations - Very Important 6 | 7 | 8 | def test_simple_cell(): 9 | card_string = "1 1 -1.0 -4 5 -6 7" 10 | card = MCNPCellCard(card_string) 11 | assert card.text_string == card_string 12 | assert card.cell_id == 1 13 | assert card.cell_density == -1.0 14 | 15 | 16 | def test_more_complex_cell(): 17 | card_string = "2 3 -14.0 (-4 5 -6 7):(9 12 13)" 18 | card = MCNPCellCard(card_string) 19 | assert card.text_string == card_string 20 | assert card.cell_id == 2 21 | assert card.cell_material_number == 3 22 | assert card.cell_density == -14.0 23 | 24 | 25 | def test_mcnp_detect_keywords_all(): 26 | string = "2 3 -14.0 1 imp:n=1 imp:p=1 u=3 fill=12 vol=150" 27 | cell_card = MCNPCellCard(string) 28 | new_string = cell_card._MCNPCellCard__detect_keywords( 29 | ["imp", "u", "fill", "vol"], string 30 | ) 31 | assert new_string == "2 3 -14.0 1 " 32 | 33 | 34 | def test_mcnp_detect_keywords_imp(): 35 | string = "2 3 -14.0 1 imp:n=1 imp:p=1 " 36 | cell_card = MCNPCellCard(string) 37 | new_string = cell_card._MCNPCellCard__detect_keywords(["imp"], string) 38 | assert new_string == "2 3 -14.0 1 " 39 | 40 | 41 | def test_mcnp_detect_keywords_uni(): 42 | string = "2 3 -14.0 1 u=1 " 43 | cell_card = MCNPCellCard(string) 44 | new_string = cell_card._MCNPCellCard__detect_keywords(["u"], string) 45 | assert new_string == "2 3 -14.0 1 " 46 | 47 | 48 | def test_mcnp_detect_keywords_fill(): 49 | string = "2 3 -14.0 1 fill=3" 50 | cell_card = MCNPCellCard(string) 51 | new_string = cell_card._MCNPCellCard__detect_keywords(["fill"], string) 52 | assert new_string == "2 3 -14.0 1 " 53 | 54 | 55 | def test_mcnp_detect_keywords_vol(): 56 | string = "2 3 -14.0 1 vol=300" 57 | cell_card = MCNPCellCard(string) 58 | new_string = cell_card._MCNPCellCard__detect_keywords(["vol"], string) 59 | assert new_string == "2 3 -14.0 1 " 60 | 61 | 62 | def test_mcnp_detect_keywords_tmp(): 63 | string = "2 3 -14.0 1 tmp=300" 64 | cell_card = MCNPCellCard(string) 65 | new_string = cell_card._MCNPCellCard__detect_keywords(["tmp"], string) 66 | assert new_string == "2 3 -14.0 1 " 67 | 68 | 69 | def test_mcnp_line_format(): 70 | string = "1 0 (-1 3 4 5 6 8 9 ( 12 13 15))" 71 | mcnp_string = mcnp_line_formatter(string) 72 | assert string == mcnp_string 73 | # note these horrendously long lines - is actually what im trying to test and furthermore 74 | # all of the standard techniques to break the string across multuple lines like 75 | # https://stackoverflow.com/questions/5437619/python-style-line-continuation-with-strings 76 | # dont actually work 77 | string = "1 0 (-1 3 4 5 6 8 9 ( 12 13 15) ( 12 13 15) ( 12 13 15) ( 12 13 15) ( 12 13 15))" 78 | result = mcnp_line_formatter(string) 79 | assert result == ( 80 | "1 0 (-1 3 4 5 6 8 9 ( 12 13 15) ( 12 13 15) ( 12 13 15) ( 12 13 15) (\n 12 13 15))" 81 | ) 82 | -------------------------------------------------------------------------------- /tests/test_mcnpformatter.py: -------------------------------------------------------------------------------- 1 | from csg2csg.MCNPFormatter import get_fortran_formatted_number 2 | 3 | 4 | def test_fortran_format(): 5 | string = "-1.0-1" 6 | number = get_fortran_formatted_number(string) 7 | assert number == -1.0e-1 8 | 9 | string = "-1.0+1" 10 | number = get_fortran_formatted_number(string) 11 | assert number == -1.0e1 12 | 13 | string = "1.0+1" 14 | number = get_fortran_formatted_number(string) 15 | assert number == 1.0e1 16 | 17 | string = "15.0-100" 18 | number = get_fortran_formatted_number(string) 19 | assert number == 1.5e-99 20 | 21 | string = "-15.0+39" 22 | number = get_fortran_formatted_number(string) 23 | assert number == -1.5e40 24 | 25 | string = "-15.0+309" 26 | number = get_fortran_formatted_number(string) 27 | assert number == -1.5e310 28 | 29 | string = "6.1000" 30 | number = get_fortran_formatted_number(string) 31 | assert number == 6.1000 32 | 33 | string = "-6.1000" 34 | number = get_fortran_formatted_number(string) 35 | assert number == -6.1000 36 | -------------------------------------------------------------------------------- /tests/test_mcnpmaterial.py: -------------------------------------------------------------------------------- 1 | from csg2csg.MCNPMaterialCard import MCNPMaterialCard 2 | 3 | 4 | def test_mcnp_material(): 5 | string = ( 6 | "29063 6.917000e-01 \n" 7 | "29065.31c 3.083000e-01 \n" 8 | ) 9 | number = 1 10 | name = "M1" 11 | matcard = MCNPMaterialCard(number, string) 12 | 13 | assert matcard.material_number == number 14 | assert matcard.material_name == name 15 | assert len(matcard.composition_dictionary) == 2 16 | 17 | assert list(matcard.composition_dictionary.keys())[0] == "29063" 18 | assert list(matcard.composition_dictionary.keys())[1] == "29065" 19 | 20 | assert list(matcard.composition_dictionary.values())[0] == 6.917000e-01 21 | assert list(matcard.composition_dictionary.values())[1] == 3.083000e-01 22 | 23 | assert len(matcard.xsid_dictionary) == 2 24 | 25 | assert list(matcard.xsid_dictionary.keys())[0] == "29063" 26 | assert list(matcard.xsid_dictionary.keys())[1] == "29065" 27 | 28 | assert list(matcard.xsid_dictionary.values())[0] == "" 29 | assert list(matcard.xsid_dictionary.values())[1] == "31c" 30 | 31 | 32 | def test_mcnp_material_with_duplicates(): 33 | string = ( 34 | "29063 2.e-00 \n" 35 | "29063 1.e-00 \n" 36 | "29065.31c 3.083000e-01 \n" 37 | ) 38 | number = 1 39 | name = "M1" 40 | matcard = MCNPMaterialCard(number, string) 41 | 42 | assert matcard.material_number == number 43 | assert matcard.material_name == name 44 | assert len(matcard.composition_dictionary) == 2 45 | 46 | assert list(matcard.composition_dictionary.keys())[0] == "29063" 47 | assert list(matcard.composition_dictionary.keys())[1] == "29065" 48 | 49 | assert list(matcard.composition_dictionary.values())[0] == 3. 50 | assert list(matcard.composition_dictionary.values())[1] == 3.083000e-01 51 | 52 | assert len(matcard.xsid_dictionary) == 2 53 | 54 | assert list(matcard.xsid_dictionary.keys())[0] == "29063" 55 | assert list(matcard.xsid_dictionary.keys())[1] == "29065" 56 | 57 | assert list(matcard.xsid_dictionary.values())[0] == "" 58 | assert list(matcard.xsid_dictionary.values())[1] == "31c" 59 | 60 | 61 | def test_mcnp_material_with_keywords(): 62 | string = ( 63 | "29063 6.917000e-01 \n" 64 | "29065.31c 3.083000e-01 \n" 65 | "hlib=.70h pnlib=70u" 66 | ) 67 | number = 1 68 | name = "M1" 69 | matcard = MCNPMaterialCard(number, string) 70 | 71 | assert matcard.material_number == number 72 | assert matcard.material_name == name 73 | assert len(matcard.composition_dictionary) == 2 74 | 75 | assert list(matcard.composition_dictionary.keys())[0] == "29063" 76 | assert list(matcard.composition_dictionary.keys())[1] == "29065" 77 | 78 | assert list(matcard.composition_dictionary.values())[0] == 6.917000e-01 79 | assert list(matcard.composition_dictionary.values())[1] == 3.083000e-01 80 | 81 | assert len(matcard.xsid_dictionary) == 2 82 | 83 | assert list(matcard.xsid_dictionary.keys())[0], "29063" 84 | assert list(matcard.xsid_dictionary.keys())[1], "29065" 85 | 86 | assert list(matcard.xsid_dictionary.values())[0] == "" 87 | assert list(matcard.xsid_dictionary.values())[1] == "31c" 88 | 89 | 90 | def test_mcnp_material_with_keyword(): 91 | string = ( 92 | "29063 6.917000e-01 \n" 93 | "29065.31c 3.083000e-01 \n" 94 | "hlib=.70h" 95 | ) 96 | number = 1 97 | name = "M1" 98 | matcard = MCNPMaterialCard(number, string) 99 | 100 | assert matcard.material_number == number 101 | assert matcard.material_name == name 102 | assert len(matcard.composition_dictionary) == 2 103 | 104 | assert list(matcard.composition_dictionary.keys())[0] == "29063" 105 | assert list(matcard.composition_dictionary.keys())[1] == "29065" 106 | 107 | assert list(matcard.composition_dictionary.values())[0] == 6.917000e-01 108 | assert list(matcard.composition_dictionary.values())[1] == 3.083000e-01 109 | 110 | assert len(matcard.xsid_dictionary) == 2 111 | 112 | assert list(matcard.xsid_dictionary.keys())[0] == "29063" 113 | assert list(matcard.xsid_dictionary.keys())[1] == "29065" 114 | 115 | assert list(matcard.xsid_dictionary.values())[0] == "" 116 | assert list(matcard.xsid_dictionary.values())[1] == "31c" 117 | -------------------------------------------------------------------------------- /tests/test_mcnpparticle.py: -------------------------------------------------------------------------------- 1 | from csg2csg.ParticleNames import ParticleNames 2 | from csg2csg.MCNPParticleNames import particleToMCNP, mcnpToParticle 3 | 4 | 5 | def test_generic(): 6 | for particle in ParticleNames: 7 | name = particleToMCNP(particle) 8 | assert isinstance(name, str) 9 | assert len(name) > 0 10 | assert mcnpToParticle(name) == particle 11 | -------------------------------------------------------------------------------- /tests/test_mcnpsurface.py: -------------------------------------------------------------------------------- 1 | from csg2csg.MCNPSurfaceCard import MCNPSurfaceCard, surface_has_transform 2 | from csg2csg.SurfaceCard import SurfaceCard 3 | 4 | 5 | def test_plane_x(): 6 | card_string = "1 px 12.0" 7 | card = MCNPSurfaceCard(card_string) 8 | assert card.text_string == card_string 9 | assert card.surface_type == SurfaceCard.SurfaceType["PLANE_X"] 10 | assert card.surface_coefficients[0] == 1.0 11 | assert card.surface_coefficients[1] == 0.0 12 | assert card.surface_coefficients[2] == 0.0 13 | assert card.surface_coefficients[3] == 12.0 14 | 15 | 16 | def test_plane_y(): 17 | card_string = "1 py 12.0" 18 | card = MCNPSurfaceCard(card_string) 19 | assert card.text_string == card_string 20 | assert card.surface_type == SurfaceCard.SurfaceType["PLANE_Y"] 21 | assert card.surface_coefficients[0] == 0.0 22 | assert card.surface_coefficients[1] == 1.0 23 | assert card.surface_coefficients[2] == 0.0 24 | assert card.surface_coefficients[3] == 12.0 25 | 26 | 27 | def test_plane_z(): 28 | card_string = "1 pz 12.0" 29 | card = MCNPSurfaceCard(card_string) 30 | assert card.text_string == card_string 31 | assert card.surface_type == SurfaceCard.SurfaceType["PLANE_Z"] 32 | assert card.surface_coefficients[0] == 0.0 33 | assert card.surface_coefficients[1] == 0.0 34 | assert card.surface_coefficients[2] == 1.0 35 | assert card.surface_coefficients[3] == 12.0 36 | 37 | 38 | def test_plane_general(): 39 | card_string = "1 p 0 0 1 15" 40 | card = MCNPSurfaceCard(card_string) 41 | assert card.text_string == card_string 42 | assert card.surface_type == SurfaceCard.SurfaceType["PLANE_GENERAL"] 43 | assert card.surface_coefficients[0] == 0.0 44 | assert card.surface_coefficients[1] == 0.0 45 | assert card.surface_coefficients[2] == 1.0 46 | assert card.surface_coefficients[3] == 15.0 47 | 48 | 49 | def test_sphere(): 50 | card_string = "15 s 0 0 1 15" 51 | card = MCNPSurfaceCard(card_string) 52 | assert card.text_string == card_string 53 | assert card.surface_type == SurfaceCard.SurfaceType["SPHERE_GENERAL"] 54 | assert card.surface_coefficients[0] == 0.0 55 | assert card.surface_coefficients[1] == 0.0 56 | assert card.surface_coefficients[2] == 1.0 57 | assert card.surface_coefficients[3] == 15.0 58 | 59 | 60 | def test_gq(): 61 | card_string = "15000 gq 1 1 0 0 0 0 1 1 1 1" 62 | card = MCNPSurfaceCard(card_string) 63 | assert card.surface_type == SurfaceCard.SurfaceType["GENERAL_QUADRATIC"] 64 | assert card.surface_id == 15000 65 | assert card.surface_coefficients[0] == 1.0 66 | assert card.surface_coefficients[1] == 1.0 67 | assert card.surface_coefficients[2] == 0.0 68 | assert card.surface_coefficients[3] == 0.0 69 | assert card.surface_coefficients[4] == 0.0 70 | assert card.surface_coefficients[5] == 0.0 71 | assert card.surface_coefficients[6] == 1.0 72 | assert card.surface_coefficients[7] == 1.0 73 | assert card.surface_coefficients[8] == 1.0 74 | assert card.surface_coefficients[9] == 1.0 75 | 76 | 77 | def test_so(): 78 | card_string = "15000 so 2.5" 79 | card = MCNPSurfaceCard(card_string) 80 | assert card.surface_type == SurfaceCard.SurfaceType["SPHERE_GENERAL"] 81 | assert card.surface_id == 15000 82 | assert card.surface_coefficients[0] == 0.0 83 | assert card.surface_coefficients[1] == 0.0 84 | assert card.surface_coefficients[2] == 0.0 85 | assert card.surface_coefficients[3] == 2.5 86 | 87 | 88 | def test_sx(): 89 | card_string = "15000 sx 3.0 2.5" 90 | card = MCNPSurfaceCard(card_string) 91 | assert card.surface_type == SurfaceCard.SurfaceType["SPHERE_GENERAL"] 92 | assert card.surface_id == 15000 93 | assert card.surface_coefficients[0] == 3.0 94 | assert card.surface_coefficients[1] == 0.0 95 | assert card.surface_coefficients[2] == 0.0 96 | assert card.surface_coefficients[3] == 2.5 97 | 98 | 99 | def test_sy(): 100 | card_string = "15000 sy 3.0 2.5" 101 | card = MCNPSurfaceCard(card_string) 102 | assert card.surface_type == SurfaceCard.SurfaceType["SPHERE_GENERAL"] 103 | assert card.surface_id == 15000 104 | assert card.surface_coefficients[0] == 0.0 105 | assert card.surface_coefficients[1] == 3.0 106 | assert card.surface_coefficients[2] == 0.0 107 | assert card.surface_coefficients[3] == 2.5 108 | 109 | 110 | def test_cz(): 111 | card_string = "15000 cz 2.5" 112 | card = MCNPSurfaceCard(card_string) 113 | assert card.surface_type == SurfaceCard.SurfaceType["CYLINDER_Z"] 114 | assert card.surface_id == 15000 115 | assert card.surface_coefficients[0] == 0.0 116 | assert card.surface_coefficients[1] == 0.0 117 | assert card.surface_coefficients[2] == 2.5 118 | 119 | 120 | def test_rpp(): 121 | card_string = "15000 rpp -1 1 -1 1 -1 1" 122 | card = MCNPSurfaceCard(card_string) 123 | assert card.surface_type == SurfaceCard.SurfaceType["MACRO_RPP"] 124 | assert card.surface_id == 15000 125 | assert card.surface_coefficients[0] == -1.0 126 | assert card.surface_coefficients[1] == 1.0 127 | assert card.surface_coefficients[2] == -1.0 128 | assert card.surface_coefficients[3] == 1.0 129 | assert card.surface_coefficients[4] == -1.0 130 | assert card.surface_coefficients[5] == 1.0 131 | 132 | 133 | def test_box(): 134 | card_string = "15000 box -1 -1 -1 2 0 0 0 2 0 0 0 2" 135 | card = MCNPSurfaceCard(card_string) 136 | assert card.surface_type == SurfaceCard.SurfaceType["MACRO_RPP"] 137 | assert card.surface_id == 15000 138 | assert card.surface_coefficients[0] == -1.0 139 | assert card.surface_coefficients[1] == 1.0 140 | assert card.surface_coefficients[2] == -1.0 141 | assert card.surface_coefficients[3] == 1.0 142 | assert card.surface_coefficients[4] == -1.0 143 | assert card.surface_coefficients[5] == 1.0 144 | 145 | 146 | def test_surfacetransform_detect(): 147 | card = "1 2 px 3" 148 | assert surface_has_transform(card) 149 | card = "1 PX 3" 150 | assert not surface_has_transform(card) 151 | 152 | 153 | def test_bounding_box_px(): 154 | card_string = "1 px 3" 155 | card = MCNPSurfaceCard(card_string) 156 | box = card.bounding_box() 157 | assert box[0] == 3 158 | assert box[1] == 3 159 | assert box[2] == 0 160 | assert box[3] == 0 161 | assert box[4] == 0 162 | assert box[5] == 0 163 | 164 | # generate a bounding box for py 165 | """ 166 | def test_bounding_box_py(): 167 | card_string = "1 py 3" 168 | card = MCNPSurfaceCard(card_string) 169 | box = card.bounding_box() 170 | assert box[0] ==0 171 | assert box[1] ==0 172 | assert box[2] ==3 173 | assert box[3] ==3 174 | assert box[4] ==0 175 | assert box[5] ==0 176 | """ 177 | -------------------------------------------------------------------------------- /tests/test_openmcmaterial.py: -------------------------------------------------------------------------------- 1 | from csg2csg.OpenMCMaterial import zaid_to_name 2 | 3 | 4 | def test_zaid_name_conversion(): 5 | name = "1001" 6 | assert zaid_to_name(name) == "H1" 7 | name = "26056" 8 | print(name[0:0], name[1:1]) 9 | assert zaid_to_name(name) == "Fe56" 10 | name = "53133" 11 | assert zaid_to_name(name) == "I133" 12 | name = "56133" 13 | assert zaid_to_name(name) == "Ba133" 14 | name = "118294" 15 | assert zaid_to_name(name) == "Og294" 16 | -------------------------------------------------------------------------------- /tests/test_particle.py: -------------------------------------------------------------------------------- 1 | from csg2csg.ParticleNames import particleToGeneric, ParticleNames 2 | 3 | 4 | def test_generic(): 5 | assert particleToGeneric("NEUTRON") is None 6 | assert "Neutron" == particleToGeneric(ParticleNames["NEUTRON"]) 7 | assert "Electron" == particleToGeneric(ParticleNames["ELECTRON"]) 8 | assert "Positron" == particleToGeneric(ParticleNames["POSITRON"]) 9 | assert "Proton" == particleToGeneric(ParticleNames["PROTON"]) 10 | assert "Deuteron" == particleToGeneric(ParticleNames["DEUTERON"]) 11 | assert "Triton" == particleToGeneric(ParticleNames["TRITON"]) 12 | assert "Alpha" == particleToGeneric(ParticleNames["ALPHA"]) 13 | assert "Positive Pion" == particleToGeneric(ParticleNames["PION_PLUS"]) 14 | assert "Negative Pion" == particleToGeneric(ParticleNames["PION_NEG"]) 15 | assert "Helion" == particleToGeneric(ParticleNames["HELION"]) 16 | assert "Negative Muon" == particleToGeneric(ParticleNames["MUON_NEG"]) 17 | -------------------------------------------------------------------------------- /tests/test_serpentmaterial.py: -------------------------------------------------------------------------------- 1 | from csg2csg.SerpentMaterialCard import SerpentMaterialCard 2 | from csg2csg.SerpentInput import SerpentInput 3 | 4 | 5 | def test_serpent_material(): 6 | string = ( 7 | "29063 6.917000e-01 \n" 8 | "29065.31c 3.083000e-01 \n" 9 | ) 10 | number = 1 11 | name = "copper" 12 | density = 8.93 13 | matcard = SerpentMaterialCard(number, name, density, string) 14 | 15 | assert matcard.density == density 16 | assert matcard.material_number == number 17 | assert matcard.material_name == name 18 | assert len(matcard.composition_dictionary) == 2 19 | 20 | assert list(matcard.composition_dictionary.keys())[0] == "29063" 21 | assert list(matcard.composition_dictionary.keys())[1] == "29065" 22 | 23 | assert list(matcard.composition_dictionary.values())[0] == 6.917000e-01 24 | assert list(matcard.composition_dictionary.values())[1] == 3.083000e-01 25 | 26 | assert len(matcard.xsid_dictionary) == 2 27 | 28 | assert list(matcard.xsid_dictionary.keys())[0] == "29063" 29 | assert list(matcard.xsid_dictionary.keys())[1] == "29065" 30 | 31 | assert list(matcard.xsid_dictionary.values())[0] == "" 32 | assert list(matcard.xsid_dictionary.values())[1] == "31c" 33 | 34 | 35 | def test_serpent_mat_input(): 36 | string = ["mat 1 8.93\n", "29063 6.917000e-01\n", "29065 3.083000e-01\n"] 37 | 38 | serpent = SerpentInput() 39 | serpent.file_lines = string 40 | serpent.process() 41 | -------------------------------------------------------------------------------- /tests/test_serpentsurface.py: -------------------------------------------------------------------------------- 1 | from csg2csg.MCNPSurfaceCard import MCNPSurfaceCard 2 | from csg2csg.SerpentSurfaceCard import serpent_cone_x 3 | 4 | 5 | def test_cone_x_up_write(): 6 | card = "1 k/x 0 0 0 0.5 1" 7 | surfcard = MCNPSurfaceCard(card) 8 | surfcard.b_box = [0, 10, 0, 0, 0, 0] 9 | result = serpent_cone_x(surfcard) 10 | assert result == " ckx 0.000000 0.000000 0.000000 0.500000 1.000000\n" 11 | 12 | 13 | def test_cone_x_down_write(): 14 | card = "1 k/x 0 0 0 0.5 -1" 15 | surfcard = MCNPSurfaceCard(card) 16 | surfcard.b_box = [0, 0, 0, 0, 0, 0] 17 | surfcard.b_box = [-10, 0, 0, 0, 0, 0] 18 | result = serpent_cone_x(surfcard) 19 | assert result == " ckx 0.000000 0.000000 0.000000 0.500000 -1.000000\n" 20 | -------------------------------------------------------------------------------- /tests/test_surface.py: -------------------------------------------------------------------------------- 1 | from csg2csg.SurfaceCard import SurfaceCard 2 | 3 | 4 | def test_simplify_plane_to_plane(): 5 | surface = SurfaceCard("") 6 | surface_coefficients = [1.0, 1.0, 0.0, 10.0] 7 | transform_id = 0 8 | surface_type = surface.SurfaceType["PLANE_GENERAL"] 9 | surface_id = 1 10 | surface.set_type(surface_id, transform_id, surface_type, surface_coefficients) 11 | surface.simplify() 12 | assert surface.surface_type == surface.SurfaceType["PLANE_GENERAL"] 13 | 14 | 15 | def test_simplify_gq_to_plane(): 16 | surface = SurfaceCard("") 17 | surface_coefficients = [0, 0, 0, 0, 0, 0, 1.0, 1.0, 0.0, -10.0] 18 | transform_id = 0 19 | surface_type = surface.SurfaceType["GENERAL_QUADRATIC"] 20 | surface_id = 1 21 | surface.set_type(surface_id, transform_id, surface_type, surface_coefficients) 22 | surface.simplify() 23 | assert surface.surface_type == surface.SurfaceType["PLANE_GENERAL"] 24 | 25 | 26 | def test_generalise(): 27 | surface = SurfaceCard("") 28 | surface_coefficients = [1.0, 1.0, 0.0, -10.0] 29 | transform_id = 0 30 | surface_type = surface.SurfaceType["PLANE_GENERAL"] 31 | surface_id = 1 32 | surface.set_type(surface_id, transform_id, surface_type, surface_coefficients) 33 | surface.generalise() 34 | assert surface.surface_type == surface.SurfaceType["GENERAL_QUADRATIC"] 35 | for i in range(6): 36 | assert surface.surface_coefficients[i] == 0.0 37 | 38 | 39 | def test_generalise_simplify_general_plane(): 40 | surface = SurfaceCard("") 41 | surface_coefficients = [1.0, 1.0, 0.0, -10.0] 42 | transform_id = 0 43 | surface_type = surface.SurfaceType["PLANE_GENERAL"] 44 | surface_id = 1 45 | surface.set_type(surface_id, transform_id, surface_type, surface_coefficients) 46 | surface.generalise() 47 | assert surface.surface_type == surface.SurfaceType["GENERAL_QUADRATIC"] 48 | for i in range(6): 49 | assert surface.surface_coefficients[i] == 0.0 50 | surface.simplify() 51 | # make sure we get a general plane back 52 | assert surface.surface_type == surface.SurfaceType["PLANE_GENERAL"] 53 | assert surface.surface_coefficients[0] == 1.0 54 | assert surface.surface_coefficients[1] == 1.0 55 | assert surface.surface_coefficients[2] == 0.0 56 | assert surface.surface_coefficients[3] == -10.0 57 | 58 | 59 | # test the surface.generalise() function 60 | def test_surface_generalise(): 61 | # first surface 62 | surface1 = SurfaceCard("") 63 | surface_coefficients = [1.0, 0.0, 0.0, -10.0] 64 | transform_id = 0 65 | surface_type = surface1.SurfaceType["PLANE_GENERAL"] 66 | surface_id = 1 67 | surface1.set_type(surface_id, transform_id, surface_type, surface_coefficients) 68 | surface1.generalise() 69 | assert surface1.surface_coefficients[6] == 1.0 70 | assert surface1.surface_coefficients[9] == 10.0 71 | 72 | 73 | # test the surface.reverse() function 74 | def test_surface_reverse(): 75 | # first surface 76 | surface1 = SurfaceCard("") 77 | surface_coefficients = [1.0, 0.0, 0.0, -10.0] 78 | transform_id = 0 79 | surface_type = surface1.SurfaceType["PLANE_GENERAL"] 80 | surface_id = 1 81 | surface1.set_type(surface_id, transform_id, surface_type, surface_coefficients) 82 | surface1.generalise() 83 | surface1.reverse() 84 | assert surface1.surface_coefficients[6] == -1.0 85 | assert surface1.surface_coefficients[9] == -10.0 86 | 87 | 88 | def test_surface_two_planes_compare(): 89 | # first surface 90 | surface1 = SurfaceCard("") 91 | surface_coefficients = [1.0, 0.0, 0.0, 10.0] 92 | transform_id = 0 93 | surface_type = surface1.SurfaceType["PLANE_GENERAL"] 94 | surface_id = 1 95 | surface1.set_type(surface_id, transform_id, surface_type, surface_coefficients) 96 | 97 | # second surface 98 | surface2 = SurfaceCard("") 99 | surface_coefficients = [-1.0, 0.0, 0.0, -10.0] 100 | transform_id = 0 101 | surface_type = surface2.SurfaceType["PLANE_GENERAL"] 102 | surface_id = 2 103 | surface2.set_type(surface_id, transform_id, surface_type, surface_coefficients) 104 | 105 | assert surface1.diff(surface2, False) == (False, False) 106 | assert surface1.diff(surface2) == (True, True) 107 | -------------------------------------------------------------------------------- /tests/test_vector.py: -------------------------------------------------------------------------------- 1 | from csg2csg.Vector import add, cross, subtract 2 | 3 | 4 | def test_subtract(): 5 | a = [1, 0, 0] 6 | b = [1, 0, 0] 7 | c = subtract(a, b) 8 | 9 | assert c[0] == 0.0 10 | assert c[1] == 0.0 11 | assert c[2] == 0.0 12 | 13 | 14 | def test_add(): 15 | a = [1, 0, 0] 16 | b = [0, 1, 0] 17 | c = add(a, b) 18 | 19 | assert c[0] == 1.0 20 | assert c[1] == 1.0 21 | assert c[2] == 0.0 22 | 23 | 24 | def test_cross(): 25 | a = [1, 0, 0] 26 | b = [0, 1, 0] 27 | c = cross(a, b) 28 | 29 | assert c[0] == 0.0 30 | assert c[1] == 0.0 31 | assert c[2] == 1.0 32 | --------------------------------------------------------------------------------