# Copyright 2019 TerraPower, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Read/write a CCCC GEODST geometry definition file.
GEODST files define fine and coarse meshes and mappings between
region numbers and mesh indices. They also store some zone
information.
File format definition is from [CCCC-IV]_.
Examples
--------
>>> geo = geodst.readBinary("GEODST")
>>> print(geo.xmesh)
>>> geo.zmesh[-1]*=2 # make a modification to data
>>> geodst.writeBinary(geo, "GEODST2")
"""
import numpy
from armi.nuclearDataIO import cccc
GEODST = "GEODST"
# See CCCC-IV documentation for definitions
FILE_SPEC_1D_KEYS = (
"IGOM",
"NZONE",
"NREG",
"NZCL",
"NCINTI",
"NCINTJ",
"NCINTK",
"NINTI",
"NINTJ",
"NINTK",
"IMB1",
"IMB2",
"JMB1",
"JMB2",
"KMB1",
"KMB2",
"NBS",
"NBCS",
"NIBCS",
"NZWBB",
"NTRIAG",
"NRASS",
"NTHPT",
"NGOP1",
"NGOP2",
"NGOP3",
"NGOP4",
)
[docs]class GeodstData(cccc.DataContainer):
"""
Data representation that can be read from or written to a GEODST file.
The region numbers in this data structure START AT 1, not zero! Thus
you must always remember the off-by-one conversion when comparing
with list or matrix indices.
Notes
-----
Analogous to a IsotxsLibrary for ISTOXS files.
"""
def __init__(self):
cccc.DataContainer.__init__(self)
# 4D data
self.xmesh = None
self.ymesh = None
self.zmesh = None
self.iintervals = None
self.jintervals = None
self.kintervals = None
# 5D data
self.regionVolumes = None
self.bucklings = None
self.boundaryConstants = None
self.internalBlackBoundaryConstants = None
self.zonesWithBlackAbs = None
self.zoneClassifications = None
self.regionZoneNumber = None
# 6d
self.coarseMeshRegions = None
# 7d
self.fineMeshRegions = None
[docs]class GeodstStream(cccc.StreamWithDataContainer):
"""
Stream for reading to/writing from with GEODST data.
Parameters
----------
geom : GeodstData
Data structure
fileName: str
path to geodst file
fileMode: str
string indicating if ``fileName`` is being read or written, and
in ascii or binary format
"""
@staticmethod
def _getDataContainer() -> GeodstData:
return GeodstData()
[docs] def readWrite(self):
"""
Step through the structure of a GEODST file and read/write it.
Logic to control which records will be present is here, which
comes directly off the File specification.
.. impl:: Tool to read and write GEODST files.
:id: I_ARMI_NUCDATA_GEODST
:implements: R_ARMI_NUCDATA_GEODST
Reading and writing GEODST files is performed using the general
nuclear data I/O functionalities described in
:need:`I_ARMI_NUCDATA`. Reading/writing a GEODST file is performed
through the following steps:
#. Read/write file ID record
#. Read/write file specifications on 1D record.
#. Based on the geometry type (``IGOM``), one of following records
are read/written:
* Slab (1), cylinder (3), or sphere (3): Read/write 1-D coarse
mesh boundaries and fine mesh intervals.
* X-Y (6), R-Z (7), Theta-R (8), uniform triangular (9),
hexagonal (10), or R-Theta (11): Read/write 2-D coarse mesh
boundaries and fine mesh intervals.
* R-Theta-Z (12, 15), R-Theta-Alpha (13, 16), X-Y-Z (14),
uniform triangular-Z (17), hexagonal-Z(18): Read/write 3-D
coarse mesh boundaries and fine mesh intervals.
#. If the geometry is not zero-dimensional (``IGOM`` > 0) and
buckling values are specified (``NBS`` > 0): Read/write geometry
data from 5D record.
#. If the geometry is not zero-dimensional (``IGOM`` > 0) and region
assignments are coarse-mesh-based (``NRASS`` = 0): Read/write
region assignments to coarse mesh interval.
#. If the geometry is not zero-dimensional (``IGOM`` > 0) and region
assignments are fine-mesh-based (``NRASS`` = 1): Read/write
region assignments to fine mesh interval.
"""
self._rwFileID()
self._rw1DRecord()
geomType = self._metadata["IGOM"]
if 0 > geomType >= 3:
self._rw2DRecord()
elif 6 <= geomType <= 11:
self._rw3DRecord()
elif geomType >= 12:
self._rw4DRecord()
if geomType > 0 or self._metadata["NBS"] > 0:
self._rw5DRecord()
if geomType > 0:
if self._metadata["NRASS"] == 0:
self._rw6DRecord()
elif self._metadata["NRASS"] == 1:
self._rw7DRecord()
def _rwFileID(self):
"""
Read/write file id record.
Notes
-----
The number 28 was actually obtained from
a hex editor and may be code specific.
"""
with self.createRecord() as record:
self._metadata["label"] = record.rwString(self._metadata["label"], 28)
def _rw1DRecord(self):
"""
Read/write File specifications on 1D record.
This record contains 27 integers.
"""
with self.createRecord() as record:
for key in FILE_SPEC_1D_KEYS:
self._metadata[key] = record.rwInt(self._metadata[key])
def _rw2DRecord(self):
"""Read/write 1-D coarse mesh boundaries and fine mesh intervals."""
with self.createRecord() as record:
self._data.xmesh = record.rwList(
self._data.xmesh, "double", self._metadata["NCINTI"] + 1
)
self._data.iintervals = record.rwList(
self._data.iintervals, "int", self._metadata["NCINTI"]
)
def _rw3DRecord(self):
"""Read/write 2-D coarse mesh boundaries and fine mesh intervals."""
with self.createRecord() as record:
self._data.xmesh = record.rwList(
self._data.xmesh, "double", self._metadata["NCINTI"] + 1
)
self._data.ymesh = record.rwList(
self._data.ymesh, "double", self._metadata["NCINTJ"] + 1
)
self._data.iintervals = record.rwList(
self._data.iintervals, "int", self._metadata["NCINTI"]
)
self._data.jintervals = record.rwList(
self._data.jintervals, "int", self._metadata["NCINTJ"]
)
def _rw4DRecord(self):
"""Read/write 3-D coarse mesh boundaries and fine mesh intervals."""
with self.createRecord() as record:
self._data.xmesh = record.rwList(
self._data.xmesh, "double", self._metadata["NCINTI"] + 1
)
self._data.ymesh = record.rwList(
self._data.ymesh, "double", self._metadata["NCINTJ"] + 1
)
self._data.zmesh = record.rwList(
self._data.zmesh, "double", self._metadata["NCINTK"] + 1
)
self._data.iintervals = record.rwList(
self._data.iintervals, "int", self._metadata["NCINTI"]
)
self._data.jintervals = record.rwList(
self._data.jintervals, "int", self._metadata["NCINTJ"]
)
self._data.kintervals = record.rwList(
self._data.kintervals, "int", self._metadata["NCINTK"]
)
def _rw5DRecord(self):
"""Read/write Geometry data from 5D record."""
with self.createRecord() as record:
self._data.regionVolumes = record.rwList(
self._data.regionVolumes, "float", self._metadata["NREG"]
)
self._data.bucklings = record.rwList(
self._data.bucklings, "float", self._metadata["NBS"]
)
self._data.boundaryConstants = record.rwList(
self._data.boundaryConstants, "float", self._metadata["NBCS"]
)
self._data.internalBlackBoundaryConstants = record.rwList(
self._data.internalBlackBoundaryConstants,
"float",
self._metadata["NIBCS"],
)
self._data.zonesWithBlackAbs = record.rwList(
self._data.zonesWithBlackAbs, "int", self._metadata["NZWBB"]
)
self._data.zoneClassifications = record.rwList(
self._data.zoneClassifications, "int", self._metadata["NZONE"]
)
self._data.regionZoneNumber = record.rwList(
self._data.regionZoneNumber, "int", self._metadata["NREG"]
)
def _rw6DRecord(self):
"""Read/write region assignments to coarse mesh interval."""
if self._data.coarseMeshRegions is None:
# initialize all-zeros here before reading now that we
# have the matrix dimension metadata available.
self._data.coarseMeshRegions = numpy.zeros(
(
self._metadata["NCINTI"],
self._metadata["NCINTJ"],
self._metadata["NCINTK"],
),
dtype=numpy.int16,
)
for ki in range(self._metadata["NCINTK"]):
with self.createRecord() as record:
self._data.coarseMeshRegions[:, :, ki] = record.rwIntMatrix(
self._data.coarseMeshRegions[:, :, ki],
self._metadata["NCINTJ"],
self._metadata["NCINTI"],
)
def _rw7DRecord(self):
"""Read/write region assignments to fine mesh interval."""
if self._data.fineMeshRegions is None:
# initialize all-zeros here before reading now that we
# have the matrix dimension metadata available.
self._data.fineMeshRegions = numpy.zeros(
(
self._metadata["NINTI"],
self._metadata["NINTJ"],
self._metadata["NINTK"],
),
dtype=numpy.int16,
)
for ki in range(self._metadata["NINTK"]):
with self.createRecord() as record:
self._data.fineMeshRegions[:, :, ki] = record.rwIntMatrix(
self._data.fineMeshRegions[:, :, ki],
self._metadata["NINTJ"],
self._metadata["NINTI"],
)
readBinary = GeodstStream.readBinary
readAscii = GeodstStream.readAscii
writeBinary = GeodstStream.writeBinary
writeAscii = GeodstStream.writeAscii