diff --git a/.gitignore b/.gitignore index 6838e75689..c8d4d7140a 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,6 @@ CMakeSettings.json # Visual Studio Code configuration files .vscode/ + +# Python pickle files +*.pkl \ No newline at end of file diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index af229bcfff..1d04bb8fbf 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -19,7 +19,7 @@ Each ```` element can have the following attributes or sub-elements: :name: An optional string name to identify the surface in summary output - files. This string is limited to 52 characters for formatting purposes. + files. *Default*: "" @@ -122,7 +122,6 @@ Each ```` element can have the following attributes or sub-elements: :name: An optional string name to identify the cell in summary output files. - This string is limmited to 52 characters for formatting purposes. *Default*: "" @@ -235,7 +234,7 @@ the following attributes or sub-elements: :name: An optional string name to identify the lattice in summary output - files. This string is limited to 52 characters for formatting purposes. + files. *Default*: "" @@ -301,7 +300,7 @@ the following attributes or sub-elements: :name: An optional string name to identify the hex_lattice in summary output - files. This string is limited to 52 characters for formatting purposes. + files. *Default*: "" @@ -370,3 +369,40 @@ Here is an example of a properly defined 2d hexagonal lattice: 202 + + +.. _dagmc_element: + +---------------------------- +```` Element +---------------------------- + +Each ```` element can have the following attributes or sub-elements: + + :id: + A unique integer used to identify the universe. + + *Default*: None + + :name: + An optional string name to identify the surface in summary output + files. + + *Default*: None + + :auto_geom_ids: + Boolean value indicating whether the existing geometry IDs will be used or appended + to the existing ID space of natively defined OpenMC geometry entities. + + *Default*: false + + :auto_mat_ids: + Boolean value indicating whether the existing material IDs will be used or appended + to the existing ID space of natively defined OpenMC materials. + + *Default*: false + + :filename: + A required string indicating the file to be loaded representing the DAGMC universe. + + *Default*: None diff --git a/docs/source/io_formats/materials.rst b/docs/source/io_formats/materials.rst index ac2cfe271e..92b0165a43 100644 --- a/docs/source/io_formats/materials.rst +++ b/docs/source/io_formats/materials.rst @@ -31,7 +31,7 @@ Each ``material`` element can have the following attributes or sub-elements: :name: An optional string name to identify the material in summary output - files. This string is limited to 52 characters for formatting purposes. + files. *Default*: "" diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 53376b04c7..6ea8cfc56e 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -88,14 +88,6 @@ you care. This element has the following attributes/sub-elements: *Default*: 0.0 --------------------------------- -```` Element --------------------------------- - -When the DAGMC mode is enabled, the OpenMC geometry will be read from the file -``dagmc.h5m``. If a :ref:`geometry.xml ` file is present with -``dagmc`` set to ``true``, it will be ignored. - ---------------------------- ```` ---------------------------- diff --git a/docs/source/io_formats/summary.rst b/docs/source/io_formats/summary.rst index cf0eca4aae..9b92b30187 100644 --- a/docs/source/io_formats/summary.rst +++ b/docs/source/io_formats/summary.rst @@ -24,8 +24,6 @@ The current version of the summary file format is 6.0. - **n_universes** (*int*) -- Number of unique universes in the problem. - **n_lattices** (*int*) -- Number of lattices in the problem. - - **dagmc** (*int*) -- Indicates that a DAGMC geometry was used - if present. **/geometry/cells/cell /** @@ -49,6 +47,8 @@ The current version of the summary file format is 6.0. - **lattice** (*int*) -- Unique ID of the lattice which fills the cell. Only present if fill_type is set to 'lattice'. - **region** (*char[]*) -- Region specification for the cell. + - **geom_type** (*char[]*) -- Type of geometry used to create the cell. + Either 'csg' or 'dagmc'. **/geometry/surfaces/surface /** @@ -62,12 +62,27 @@ The current version of the summary file format is 6.0. - **boundary_condition** (*char[]*) -- Boundary condition applied to the surface. Can be 'transmission', 'vacuum', 'reflective', or 'periodic'. + - **geom_type** (*char[]*) -- Type of geometry used to create the cell. + Either 'csg' or 'dagmc'. + **/geometry/universes/universe /** :Datasets: - **cells** (*int[]*) -- Array of unique IDs of cells that appear in the universe. + - **geom_type** (*char[]*) -- Type of geometry used to create the cell. + Either 'csg' or 'dagmc'. + - **filename** (*char[]*) -- Name of the DAGMC file representing this universe. + Only present for DAGMC Universes. +:Attributes: + - **auto_geom_ids** (*int*) -- ``1`` if geometry IDs of the DAGMC + model will be appended to the ID space of the natively defined + CSG geometry, ``0`` if the existing DAGMC IDs will be used. + - **auto_mat_ids** (*int*) -- ``1`` if UWUW material IDs of the DAGMC + model will be appended to the ID space of the natively defined + OpenMC materials, ``0`` if the existing UWUW IDs will be used. + **/geometry/lattices/lattice /** @@ -119,8 +134,8 @@ The current version of the summary file format is 6.0. :Attributes: - **volume** (*double[]*) -- Volume of this material [cm^3]. Only present if ``volume`` supplied - **temperature** (*double[]*) -- Temperature of this material [K]. - Only present in ``temperature`` supplied - - **depletable** (*int[]*) -- ``1`` if the material can be depleted, + Only present if ``temperature`` is supplied + - **depletable** (*int*) -- ``1`` if the material can be depleted, ``0`` otherwise. Always present **/nuclides/** diff --git a/docs/source/io_formats/tallies.rst b/docs/source/io_formats/tallies.rst index 8c1ad91b58..ad72273690 100644 --- a/docs/source/io_formats/tallies.rst +++ b/docs/source/io_formats/tallies.rst @@ -31,7 +31,7 @@ The ```` element accepts the following sub-elements: :name: An optional string name to identify the tally in summary output - files. This string is limited to 52 characters for formatting purposes. + files. *Default*: "" diff --git a/docs/source/usersguide/geometry.rst b/docs/source/usersguide/geometry.rst index c8e5fd99f3..2b6a6fef2f 100644 --- a/docs/source/usersguide/geometry.rst +++ b/docs/source/usersguide/geometry.rst @@ -433,30 +433,64 @@ will handle creating the unverse:: Using CAD-based Geometry -------------------------- -OpenMC relies on the Direct Accelerated Geometry Monte Carlo toolkit (`DAGMC -`_) to represent CAD-based geometry in a -surface mesh format. A DAGMC run can be enabled in OpenMC by setting the -``dagmc`` property to ``True`` in the model Settings either via the Python -:class:`openmc.settings` Python class:: +Defining Geometry +----------------- - settings = openmc.Settings() - settings.dagmc = True +OpenMC relies on the `Direct Accelerated Geometry Monte Carlo`_ (DAGMC) +to represent CAD-based geometry in a surface mesh format. DAGMC geometries are +applied as universes in the OpenMC geometry file. A geometry represented +entirely by a DAGMC geometry will contain only the DAGMC universe. Using a +:class:`openmc.DAGMCUniverse` looks like the following:: -or in the :ref:`settings.xml ` file:: + dag_univ = openmc.DAGMCUniverse(filename='dagmc.h5m') + geometry = openmc.Geometry(dag_univ) + geometry.export_to_xml() - true +The resulting ``geometry.xml`` file will be: -With ``dagmc`` set to true, OpenMC will load the DAGMC model (from a local file -named ``dagmc.h5m``) when initializing a simulation. If a `geometry.xml -<../io_formats/geometry.html>`_ is present as well, it will be ignored. +.. code-block:: xml - **Note:** DAGMC geometries used in OpenMC are currently required to be clean, - meaning that all surfaces have been `imprinted and merged - `_ - successfully and that the model is `watertight - `_. Future - implementations of DAGMC geometry will support small volume overlaps and - un-merged surfaces. + + + + + +DAGMC universes can also be used to fill CSG cells or lattice cells in a geometry:: + + cell.fill = dagmc_univ + +It is important in these cases to understand the DAGMC model's position +with respect to the CSG geometry. DAGMC geometries can be plotted with +OpenMC to verify that the model matches one's expectations. + +**Note:** DAGMC geometries used in OpenMC are currently required to be clean, +meaning that all surfaces have been `imprinted and merged +`_ +successfully and that the model is `watertight +`_. Future +implementations of DAGMC geometry will support small volume overlaps and +un-merged surfaces. + +Cell, Surface, and Material IDs +------------------------------- + +By default, DAGMC applies cell and surface IDs defined by the CAD engine that +the model originated in. If these IDs overlap with IDs in the CSG ID space, +this will result in an error. However, the ``auto_ids`` property of a DAGMC +universe can be set to set DAGMC cell and surface IDs by appending to the +existing CSG cell ID space in the OpenMC model. + +Similar options exist for the material IDs of DAGMC models. If DAGMC material +assignments are based on natively defined OpenMC materials, no further work is +required. If DAGMC materials are assigned using the `University of Wisconsin +Unified Workflow`_ (UWUW), however, material IDs in the UWUW material library +may overlap with those used in the CSG geometry. In this case, overlaps in the +UWUW and OpenMC material ID space will cause an error. To automatically resolve +these ID overlaps, ``auto_ids`` can be set to ``True`` to append the UWUW +material IDs to the OpenMC material ID space. + +.. _Direct Accelerated Geometry Monte Carlo: https://svalinn.github.io/DAGMC/ +.. _University of Wisconsin Unified Workflow: https://svalinn.github.io/DAGMC/usersguide/uw2.html ------------------------- Calculating Atoms Content diff --git a/examples/jupyter/cad-based-geometry.ipynb b/examples/jupyter/cad-based-geometry.ipynb index 3699360a63..99be260c7f 100644 --- a/examples/jupyter/cad-based-geometry.ipynb +++ b/examples/jupyter/cad-based-geometry.ipynb @@ -141,7 +141,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "OpenMC expects that the model has the name \"dagmc.h5m\" so we'll name the file that and indicate to OpenMC that a DAGMC geometry is being used by setting the `settings.dagmc` attribute to `True`." + "To create a geometry where DAGMC represents the entire model, we'll make a DAGMC universe and use it as the root universe of the model." ] }, { @@ -149,9 +149,19 @@ "execution_count": 6, "metadata": {}, "outputs": [], + "source": [ + "dagmc_univ = openmc.DAGMCUniverse(filename=\"dagmc.h5m\")\n", + "geometry = openmc.Geometry(root=dagmc_univ)\n", + "geometry.export_to_xml()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], "source": [ "settings = openmc.Settings()\n", - "settings.dagmc = True\n", "settings.batches = 10\n", "settings.inactive = 2\n", "settings.particles = 5000\n", @@ -169,12 +179,12 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQAgMAAAD90d5fAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACVBMVEX///8AAP///wCN6GYzAAAAAWJLR0QAiAUdSAAAAAd0SU1FB+UGHg4BOmBSMGQAAAWvSURBVHja7Z3NddswEISjA0pgPyqBB9MHlsB+UgIPUpV5tmPiRwSwfxg6CPacvC87M4D0InL3169Ro0aNGjVq1KhRP7Buy2fdGyKm5ai3Rgi3RDU3bqNZM8tJIRjWlOkcYqpYhmFKcUu2ZivGbSnU3QiyFMuGMZUhJrbclkpZCFZjWAjm6pAZ0Ii+FUIj6laqrlt4P9EgqhgTG9G1QnJE6wqVoQkYuRFNK3SGvBVGI/JWJg5EmGJyfr/q3l4tqV48hsx6ZiOyVli2S63nMiR6ZdR6fpaVXmdqvT+P+m2h19kh2Z5BPU7+wF2v1jMpvV5TlXFCYer1qtb2CnlVjKeXIzBOKDy9UrXW52ntKr3y2Y0rTbJGrS0HSQXj6JWotT6zlQjG0aua3myO6YwkwFsJkgh2F1ry/ixW7D3dlIneSNoK3RROI2krMku2GiRuhWqKYzWStEI1ZWI1krRCNYV8Rk7PisCSlQKJjj3NFMdtJG6FZkpoCcH21HqaKUzbX6xnW0JjxHpRTAktIdmeWk8xZeKrFetFMYVve2o9zxKyWrFedVOcRK1Yr7opk0itSK+6KZNIrUivOkSmVqwXx3cOIzqPNeedUK1Ir5rzk1CtSK+aKZNUrVCvGkQY4I8KQkz2nWlJZErZ+cB3piWRKTMVwmWEppQh3ne2JaEpZecVlkSmECFsSyJTiOHiM0JTSvHyvgssCU0pOe99F1gSmvJGgggsCU0pQXSWhKZQICJLQlMo4RJZEpqSj5ezg8wEyCaDPAgQHy4ZI3A+Hy+t7yTntZaEpjQLFyVeHrJJIY8qxIdLygicn2sQse+B8znIZAl5q0FWOWSvQfS+h843C1c9Xh4iZwTxOodYhKsaLwjEIlzVeB2QTQN5/ACISbiCeF0GOY6JKlxBvO4lyKqD7CWIs4bMJcimgzxKEJsEVzJ8QHQMH6/LIEYJDjJ8EcTqmBQPChayaSGPPMTqLBZP4wHRMnyGL4KYncXSafyGqI+JPygXQcyOSXBQ+oXYHfjCkcdC9Ax/UC6BGN4q+XvlG2JwFv1pvARieKvk75VvyGoB2buHGF5d+cvL8FYJ7pXuIRYMf680vB9zN2R/EJOry19eF0BMb/rcXd8fZLWB7AOih5h+MOY+GqGQzQbyGJAB6RGytIEspxAbRuaL14AMyIAMyIAMyIAMyIBgvtxNbSC9fuEekP8cstpA9usgrg1kHhA9pJ//F24K6edXh/5+CWoKgfwE2OEvphDIagHZMxDTj8bzD8YeIU0fKTG9689vejCk6QNL/Tzf1c8zd/08B4mBGN4ruVsFDGn6ULLhvZK7VdCQTcsoPPIOeXi/n3cd+nn/pKN3gg7IpmMUX6GyOo3Fl8EOyKqD7CUI5AU9yKuG/byZ2dHbsgdk1UD2MuQ4KP/8u9iQV9chL+FDxglgBiMckJYjHiziVQkXaOwGZIAIZBQKZKgLZjzNkeGWg3Z8vDYZgzIyyGlN2QkQyBgnyEAqzGgtH69NwqANCZt0puwkiNOZ4i2ZCxDICDrIMD3MWEDvfMMBh975hqMaAwjbFPLQScj4TMggUMxI0wDCNIUxnDVwnmlKYMlcgUAG5kJG/2KGGAeQduOYnUwv3mBpyIhsyLBvzNhyJ9ErVKtuCWiUPGQoPma8v+PrFf4NiiWglQuQ5RGYNRiRKa0WekBWk0CWrGDWxThOK3EjVEtAK3wgy4gwa5Vc8u/LCqZZEAVZdYVZ2pXq1WT9GGSRGmQlHGa5HWRNH2ThIGZ1ImQJJGadpeNCBGphVoxilqVC1r5CFthiVvFClgpj1iNDFj1jVlZDlm9D1ohjFqJDVrtjltRTWlE3QmlFz6h7fzeA1GKsiy9RMBtGWTATsT6qkLDZilGwxciQIsWUkTPflnFOsWacKGas1VclGZtbMOJmmrTxt27GJ3DUqFGjRo0aNWqUZf0BnVivRTb4g0AAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDYtMzBUMTQ6MDE6NTgrMDA6MDAcifJ4AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTA2LTMwVDE0OjAxOjU4KzAwOjAwbdRKxAAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] @@ -201,7 +211,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -219,7 +229,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -246,7 +256,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": { "scrolled": true }, @@ -283,15 +293,16 @@ " Copyright | 2011-2021 MIT and OpenMC contributors\n", " License | https://docs.openmc.org/en/latest/license.html\n", " Version | 0.13.0-dev\n", - " Git SHA1 | 3dd81a1316ac3b5a0633e4b7a290f3bc97a066d9\n", - " Date/Time | 2021-06-26 11:58:36\n", + " Git SHA1 | 0aafa81907ac79b9bdb4a86aede0b63fee3a9a9b\n", + " Date/Time | 2021-06-30 09:01:58\n", " OpenMP Threads | 2\n", "\n", " Reading settings XML file...\n", " Reading cross sections XML file...\n", " Reading materials XML file...\n", - " Reading DAGMC geometry...\n", - "Using the DOUBLE-DOWN interface to Embree.\n", + " Reading geometry XML file...\n", + "Set overlap thickness = 0\n", + "Set numerical precision = 0.001\n", "Loading file dagmc.h5m\n", "Initializing the GeomQueryTool...\n", "Using faceting tolerance: 0.0001\n", @@ -327,21 +338,21 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 1.4751e-01 seconds\n", - " Reading cross sections = 1.0076e-01 seconds\n", - " Total time in simulation = 3.0461e-01 seconds\n", - " Time in transport only = 2.9733e-01 seconds\n", - " Time in inactive batches = 5.7728e-02 seconds\n", - " Time in active batches = 2.4689e-01 seconds\n", - " Time synchronizing fission bank = 2.9442e-03 seconds\n", - " Sampling source sites = 2.6400e-03 seconds\n", - " SEND/RECV source sites = 3.0032e-04 seconds\n", - " Time accumulating tallies = 4.2046e-05 seconds\n", - " Time writing statepoints = 3.0596e-03 seconds\n", - " Total time for finalization = 8.3508e-05 seconds\n", - " Total time elapsed = 4.5681e-01 seconds\n", - " Calculation Rate (inactive) = 173226.0 particles/second\n", - " Calculation Rate (active) = 162018.0 particles/second\n", + " Total time for initialization = 2.0815e-01 seconds\n", + " Reading cross sections = 9.2095e-02 seconds\n", + " Total time in simulation = 1.5220e+00 seconds\n", + " Time in transport only = 1.5148e+00 seconds\n", + " Time in inactive batches = 2.9113e-01 seconds\n", + " Time in active batches = 1.2309e+00 seconds\n", + " Time synchronizing fission bank = 3.1554e-03 seconds\n", + " Sampling source sites = 2.7998e-03 seconds\n", + " SEND/RECV source sites = 3.4967e-04 seconds\n", + " Time accumulating tallies = 3.0953e-05 seconds\n", + " Time writing statepoints = 2.7201e-03 seconds\n", + " Total time for finalization = 2.3371e-04 seconds\n", + " Total time elapsed = 1.7509e+00 seconds\n", + " Calculation Rate (inactive) = 34348.7 particles/second\n", + " Calculation Rate (active) = 32497.7 particles/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -374,7 +385,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -383,7 +394,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -393,7 +404,7 @@ "" ] }, - "execution_count": 12, + "execution_count": 13, "metadata": { "image/jpeg": { "width": 600 @@ -415,7 +426,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -438,12 +449,12 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "" ] @@ -472,12 +483,12 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "" ] @@ -504,12 +515,11 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "settings = openmc.Settings()\n", - "settings.dagmc = True\n", "settings.batches = 10\n", "settings.particles = 5000\n", "settings.run_mode = \"fixed source\"\n", @@ -543,12 +553,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "...and setup a couple mesh tallies. One for the kettle, and one for the water inside." + "...and setup a couple of mesh tallies. One for the kettle, and one for the water inside." ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -576,7 +586,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -611,15 +621,16 @@ " Copyright | 2011-2021 MIT and OpenMC contributors\n", " License | https://docs.openmc.org/en/latest/license.html\n", " Version | 0.13.0-dev\n", - " Git SHA1 | 3dd81a1316ac3b5a0633e4b7a290f3bc97a066d9\n", - " Date/Time | 2021-06-26 11:58:41\n", + " Git SHA1 | 0aafa81907ac79b9bdb4a86aede0b63fee3a9a9b\n", + " Date/Time | 2021-06-30 09:02:13\n", " OpenMP Threads | 2\n", "\n", " Reading settings XML file...\n", " Reading cross sections XML file...\n", " Reading materials XML file...\n", - " Reading DAGMC geometry...\n", - "Using the DOUBLE-DOWN interface to Embree.\n", + " Reading geometry XML file...\n", + "Set overlap thickness = 0\n", + "Set numerical precision = 0.001\n", "Loading file dagmc.h5m\n", "Initializing the GeomQueryTool...\n", "Using faceting tolerance: 0.001\n", @@ -655,16 +666,16 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 9.0900e-01 seconds\n", - " Reading cross sections = 4.7368e-01 seconds\n", - " Total time in simulation = 4.5284e+00 seconds\n", - " Time in transport only = 4.5248e+00 seconds\n", - " Time in active batches = 4.5284e+00 seconds\n", - " Time accumulating tallies = 2.4557e-04 seconds\n", - " Time writing statepoints = 3.1711e-03 seconds\n", - " Total time for finalization = 1.4047e-02 seconds\n", - " Total time elapsed = 5.4540e+00 seconds\n", - " Calculation Rate (active) = 11041.4 particles/second\n", + " Total time for initialization = 4.8117e+00 seconds\n", + " Reading cross sections = 4.6543e-01 seconds\n", + " Total time in simulation = 1.4498e+01 seconds\n", + " Time in transport only = 1.4493e+01 seconds\n", + " Time in active batches = 1.4498e+01 seconds\n", + " Time accumulating tallies = 3.1628e-04 seconds\n", + " Time writing statepoints = 4.8840e-03 seconds\n", + " Total time for finalization = 1.5489e-02 seconds\n", + " Total time elapsed = 1.9328e+01 seconds\n", + " Calculation Rate (active) = 3448.64 particles/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -686,34 +697,37 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ - "with openmc.StatePoint(\"statepoint.10.h5\") as sp:\n", - " water_tally = sp.get_tally(scores=['flux'], id=water_tally.id)\n", - " water_flux = water_tally.mean\n", - " water_flux.shape = (40, 120)\n", - " water_flux = water_flux[::-1, :]\n", + "sp = openmc.StatePoint(\"statepoint.10.h5\")\n", "\n", - " pot_tally = sp.get_tally(scores=['flux'], id=pot_tally.id)\n", - " pot_flux = pot_tally.mean\n", - " pot_flux.shape = (40, 120)\n", - " pot_flux = pot_flux[::-1, :]" + "water_tally = sp.get_tally(scores=['flux'], id=water_tally.id)\n", + "water_flux = water_tally.mean\n", + "water_flux.shape = (40, 120)\n", + "water_flux = water_flux[::-1, :]\n", + "\n", + "pot_tally = sp.get_tally(scores=['flux'], id=pot_tally.id)\n", + "pot_flux = pot_tally.mean\n", + "pot_flux.shape = (40, 120)\n", + "pot_flux = pot_flux[::-1, :]\n", + "\n", + "del sp" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 20, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, diff --git a/examples/jupyter/unstructured-mesh-part-ii.ipynb b/examples/jupyter/unstructured-mesh-part-ii.ipynb index 478417970e..3dcc9d418a 100644 --- a/examples/jupyter/unstructured-mesh-part-ii.ipynb +++ b/examples/jupyter/unstructured-mesh-part-ii.ipynb @@ -181,14 +181,15 @@ "\n", "settings.run_mode = \"fixed source\"\n", "settings.batches = 10\n", - "settings.particles = 100" + "settings.particles = 100\n", + "settings.export_to_xml()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "And we'll indicate that we're using a CAD-based geometry." + "Next we'll apply the DAGMC model as the root universe of the geometry." ] }, { @@ -197,9 +198,9 @@ "metadata": {}, "outputs": [], "source": [ - "settings.dagmc = True\n", - "\n", - "settings.export_to_xml()" + "dagmc_univ = openmc.DAGMCUniverse(filename='dagmc.h5m')\n", + "geometry = openmc.Geometry(root=dagmc_univ)\n", + "geometry.export_to_xml()" ] }, { @@ -246,15 +247,15 @@ " Copyright | 2011-2020 MIT and OpenMC contributors\n", " License | https://docs.openmc.org/en/latest/license.html\n", " Version | 0.12.1-dev\n", - " Git SHA1 | 8ae407c90e927af62bfdc8f150e96bfd5d7b2ec2\n", - " Date/Time | 2021-01-06 09:17:05\n", + " Git SHA1 | 9fb298b039bcd447c1a745cd9fea47f0d04eebdf\n", + " Date/Time | 2021-03-04 13:34:25\n", " MPI Processes | 1\n", - " OpenMP Threads | 2\n", + " OpenMP Threads | 4\n", "\n", " Reading settings XML file...\n", " Reading cross sections XML file...\n", " Reading materials XML file...\n", - " Reading DAGMC geometry...\n", + " Reading geometry XML file...\n", "Set overlap thickness = 0\n", "Set numerical precision = 0.001\n", "Loading file dagmc.h5m\n", @@ -317,16 +318,16 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 2.4196e+01 seconds\n", - " Reading cross sections = 2.1428e+00 seconds\n", - " Total time in simulation = 1.9512e-01 seconds\n", - " Time in transport only = 1.9269e-01 seconds\n", - " Time in active batches = 1.9512e-01 seconds\n", - " Time accumulating tallies = 2.0220e-06 seconds\n", - " Time writing statepoints = 2.2461e-03 seconds\n", - " Total time for finalization = 1.8940e-06 seconds\n", - " Total time elapsed = 2.4401e+01 seconds\n", - " Calculation Rate (active) = 5125.02 particles/second\n", + " Total time for initialization = 1.7970e+02 seconds\n", + " Reading cross sections = 1.8083e+00 seconds\n", + " Total time in simulation = 7.2061e-01 seconds\n", + " Time in transport only = 7.1887e-01 seconds\n", + " Time in active batches = 7.2061e-01 seconds\n", + " Time accumulating tallies = 2.7050e-06 seconds\n", + " Time writing statepoints = 1.5381e-03 seconds\n", + " Total time for finalization = 1.1780e-06 seconds\n", + " Total time elapsed = 1.8043e+02 seconds\n", + " Calculation Rate (active) = 1387.71 particles/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -352,7 +353,7 @@ "metadata": {}, "outputs": [], "source": [ - "unstructured_mesh = openmc.UnstructuredMesh(\"manifold.h5m\", library='moab')\n", + "unstructured_mesh = openmc.UnstructuredMesh(\"manifold.h5m\")\n", "\n", "mesh_filter = openmc.MeshFilter(unstructured_mesh)\n", "\n", @@ -496,7 +497,7 @@ "text": [ "\r\n", "\r\n", - " \r\n", + " \r\n", " manifold.h5m\r\n", " \r\n", " \r\n", diff --git a/include/openmc/cell.h b/include/openmc/cell.h index c7ba0ab137..9e977bc937 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -10,7 +10,6 @@ #include #include "hdf5.h" #include "pugixml.hpp" -#include "dagmc.h" #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr @@ -63,16 +62,25 @@ namespace model { class Universe { public: + int32_t id_; //!< Unique ID vector cells_; //!< Cells within this universe //! \brief Write universe information to an HDF5 group. //! \param group_id An HDF5 group id. - void to_hdf5(hid_t group_id) const; + virtual void to_hdf5(hid_t group_id) const; + + virtual bool find_cell(Particle &p) const; BoundingBox bounding_box() const; + const GeometryType& geom_type() const { return geom_type_; } + GeometryType& geom_type() { return geom_type_; } + unique_ptr partitioner_; + +private: + GeometryType geom_type_ = GeometryType::CSG; }; //============================================================================== @@ -118,7 +126,9 @@ public: //! Write all information needed to reconstruct the cell to an HDF5 group. //! \param group_id An HDF5 group id. - virtual void to_hdf5(hid_t group_id) const = 0; + void to_hdf5(hid_t group_id) const; + + virtual void to_hdf5_inner(hid_t group_id) const = 0; //! Get the BoundingBox for this cell. virtual BoundingBox bounding_box() const = 0; @@ -164,10 +174,11 @@ public: int32_t id_; //!< Unique ID std::string name_; //!< User-defined name - Fill type_; //!< Material, universe, or lattice + Fill type_; //!< Material, universe, or lattice int32_t universe_; //!< Universe # this cell is in int32_t fill_; //!< Universe # filling this cell int32_t n_instances_{0}; //!< Number of instances of this cell + GeometryType geom_type_; //!< Geometric representation type (CSG, DAGMC) //! \brief Index corresponding to this cell in distribcell arrays int distribcell_index_{C_NONE}; @@ -225,7 +236,7 @@ public: std::pair distance(Position r, Direction u, int32_t on_surface, Particle* p) const; - void to_hdf5(hid_t group_id) const; + void to_hdf5_inner(hid_t group_id) const override; BoundingBox bounding_box() const; @@ -254,28 +265,6 @@ protected: vector::iterator start, const vector& rpn); }; -//============================================================================== - -#ifdef DAGMC -class DAGCell : public Cell -{ -public: - DAGCell(); - - bool contains(Position r, Direction u, int32_t on_surface) const; - - std::pair - distance(Position r, Direction u, int32_t on_surface, Particle* p) const; - - BoundingBox bounding_box() const; - - void to_hdf5(hid_t group_id) const; - - moab::DagMC* dagmc_ptr_; //!< Pointer to DagMC instance - int32_t dag_index_; //!< DagMC index of cell -}; -#endif - //============================================================================== //! Speeds up geometry searches by grouping cells in a search tree. // @@ -342,8 +331,9 @@ struct CellInstanceHash { void read_cells(pugi::xml_node node); + #ifdef DAGMC -int32_t next_cell(DAGCell* cur_cell, DAGSurface* surf_xed); +class DAGUniverse; #endif } // namespace openmc diff --git a/include/openmc/constants.h b/include/openmc/constants.h index aac3a52a83..89afadeb43 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -379,6 +379,15 @@ enum class RunMode { // For non-accelerated regions on coarse mesh overlay constexpr int CMFD_NOACCEL {-1}; +//============================================================================== +// Geometry Constants + +enum class GeometryType { + CSG, + DAG +}; + + } // namespace openmc #endif // OPENMC_CONSTANTS_H diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 9f409d8730..6a71e61702 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -1,4 +1,3 @@ - #ifndef OPENMC_DAGMC_H #define OPENMC_DAGMC_H @@ -6,27 +5,146 @@ namespace openmc { extern "C" const bool DAGMC_ENABLED; } -#ifdef DAGMC - -#include "DagMC.hpp" +// always include the XML interface header #include "openmc/xml_interface.h" -#include "openmc/position.h" + +//============================================================================== +// Functions that are always defined +//============================================================================== namespace openmc { -namespace model { - extern moab::DagMC* DAG; +void read_dagmc_universes(pugi::xml_node node); +void check_dagmc_root_univ(); + } +#ifdef DAGMC + +#include "DagMC.hpp" + +#include "openmc/cell.h" +#include "openmc/particle.h" +#include "openmc/position.h" +#include "openmc/surface.h" + +class UWUW; + +namespace openmc { + +class DAGSurface : public Surface { +public: + DAGSurface(std::shared_ptr dag_ptr, int32_t dag_idx); + + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + Direction reflect(Position r, Direction u, Particle* p) const override; + + inline void to_hdf5_inner(hid_t group_id) const override {}; + + // Accessor methods + const std::shared_ptr& dagmc_ptr() const { return dagmc_ptr_; } + int32_t dag_index() const { return dag_index_; } + +private: + std::shared_ptr dagmc_ptr_; //!< Pointer to DagMC instance + int32_t dag_index_; //!< DagMC index of surface +}; + +class DAGCell : public Cell { +public: + DAGCell(std::shared_ptr dag_ptr, int32_t dag_idx); + + bool contains(Position r, Direction u, int32_t on_surface) const override; + + std::pair + distance(Position r, Direction u, int32_t on_surface, Particle* p) const override; + + BoundingBox bounding_box() const override; + + void to_hdf5_inner(hid_t group_id) const override; + + // Accessor methods + const std::shared_ptr& dagmc_ptr() const { return dagmc_ptr_; } + int32_t dag_index() const { return dag_index_; } + +private: + std::shared_ptr dagmc_ptr_; //!< Pointer to DagMC instance + int32_t dag_index_; //!< DagMC index of cell +}; + +class DAGUniverse : public Universe { + +public: + + explicit DAGUniverse(pugi::xml_node node); + + //! Create a new DAGMC universe + //! \param[in] filename Name of the DAGMC file + //! \param[in] auto_geom_ids Whether or not to automatically assign cell and surface IDs + //! \param[in] auto_mat_ids Whether or not to automatically assign material IDs + explicit DAGUniverse(const std::string& filename, + bool auto_geom_ids = false, + bool auto_mat_ids = false); + + //! Initialize the DAGMC accel. data structures, indices, material assignments, etc. + void initialize(); + + //! Reads UWUW materials and returns an ID map + void read_uwuw_materials(); + //! Indicates whether or not UWUW materials are present + //! \return True if UWUW materials are present, False if not + bool uses_uwuw() const; + + //! Returns the index to the implicit complement's index in OpenMC for this DAGMC universe + int32_t implicit_complement_idx() const; + + //! Transform UWUW materials into an OpenMC-readable XML format + //! \return A string representing a materials.xml file of the UWUW materials in this universe + std::string get_uwuw_materials_xml() const; + + //! Writes the UWUW material file to XML (for debugging purposes) + void write_uwuw_materials_xml(const std::string& outfile = "uwuw_materials.xml") const; + + //! Assign a material to a cell based + //! \param[in] mat_string The DAGMC material assignment string + //! \param[in] c The OpenMC cell to which the material is assigned + void legacy_assign_material(std::string mat_string, + std::unique_ptr& c) const; + + //! Generate a string representing the ranges of IDs present in the DAGMC model. + //! Contiguous chunks of IDs are represented as a range (i.e. 1-10). If there is + //! a single ID a chunk, it will be represented as a single number (i.e. 2, 4, 6, 8). + //! \param[in] dim Dimension of the entities + //! \return A string of the ID ranges for entities of dimension \p dim + std::string dagmc_ids_for_dim(int dim) const; + + bool find_cell(Particle &p) const override; + + void to_hdf5(hid_t universes_group) const override; + + // Data Members + std::shared_ptr dagmc_instance_; //!< DAGMC Instance for this universe + int32_t cell_idx_offset_; //!< An offset to the start of the cells in this universe in OpenMC's cell vector + int32_t surf_idx_offset_; //!< An offset to the start of the surfaces in this universe in OpenMC's surface vector + + // Accessors + bool has_graveyard() const { return has_graveyard_; } + +private: + std::string filename_; //!< Name of the DAGMC file used to create this universe + std::shared_ptr uwuw_; //!< Pointer to the UWUW instance for this universe + bool adjust_geometry_ids_; //!< Indicates whether or not to automatically generate new cell and surface IDs for the universe + bool adjust_material_ids_; //!< Indicates whether or not to automatically generate new material IDs for the universe + bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" volume +}; + //============================================================================== // Non-member functions //============================================================================== -void load_dagmc_geometry(); -void free_memory_dagmc(); -void read_geometry_dagmc(); -bool read_uwuw_materials(pugi::xml_document& doc); -bool get_uwuw_materials_xml(std::string& s); +int32_t next_cell(DAGUniverse* dag_univ, DAGCell* cur_cell, DAGSurface* surf_xed); } // namespace openmc diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 384f629b0f..cc3887f8f8 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -29,7 +29,6 @@ extern bool check_overlaps; //!< check overlaps in geometry? extern bool confidence_intervals; //!< use confidence intervals for results? extern bool create_fission_neutrons; //!< create fission neutrons (fixed source)? extern "C" bool cmfd_run; //!< is a CMFD run? -extern "C" bool dagmc; //!< indicator of DAGMC geometry extern bool delayed_photon_scaling; //!< Scale fission photon yield to include delayed extern "C" bool entropy_on; //!< calculate Shannon entropy? extern bool event_based; //!< use event-based mode (instead of history-based) diff --git a/include/openmc/surface.h b/include/openmc/surface.h index 5a8a88aa41..b3f2bdfad6 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -8,7 +8,6 @@ #include "hdf5.h" #include "pugixml.hpp" -#include "dagmc.h" #include "openmc/boundary_condition.h" #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr @@ -30,7 +29,7 @@ namespace model { } // namespace model //============================================================================== -//! Coordinates for an axis-aligned cuboid bounding a geometric object. +//! Coordinates for an axis-aligned cuboid that bounds a geometric object. //============================================================================== struct BoundingBox @@ -88,6 +87,7 @@ public: int id_; //!< Unique ID std::string name_; //!< User-defined name std::shared_ptr bc_ {nullptr}; //!< Boundary condition + GeometryType geom_type_; //!< Geometry type indicator (CSG or DAGMC) bool surf_source_ {false}; //!< Activate source banking for the surface? explicit Surface(pugi::xml_node surf_node); @@ -134,10 +134,13 @@ public: //! Write all information needed to reconstruct the surface to an HDF5 group. //! \param group_id An HDF5 group id. - virtual void to_hdf5(hid_t group_id) const = 0; + void to_hdf5(hid_t group_id) const; //! Get the BoundingBox for this surface. virtual BoundingBox bounding_box(bool /*pos_side*/) const { return {}; } + +protected: + virtual void to_hdf5_inner(hid_t group_id) const = 0; }; class CSGSurface : public Surface @@ -146,33 +149,10 @@ public: explicit CSGSurface(pugi::xml_node surf_node); CSGSurface(); - void to_hdf5(hid_t group_id) const; - protected: virtual void to_hdf5_inner(hid_t group_id) const = 0; }; -//============================================================================== -//! A `Surface` representing a DAGMC-based surface in DAGMC. -//============================================================================== -#ifdef DAGMC -class DAGSurface : public Surface -{ -public: - DAGSurface(); - - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - Direction reflect(Position r, Direction u, Particle* p) const; - - void to_hdf5(hid_t group_id) const; - - moab::DagMC* dagmc_ptr_; //!< Pointer to DagMC instance - int32_t dag_index_; //!< DagMC index of surface -}; -#endif - //============================================================================== //! A plane perpendicular to the x-axis. // diff --git a/openmc/cell.py b/openmc/cell.py index c55238df86..af1a07f9a9 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -27,7 +27,7 @@ class Cell(IDManagerMixin): automatically be assigned. name : str, optional Name of the cell. If not specified, the name is the empty string. - fill : openmc.Material or openmc.Universe or openmc.Lattice or None or iterable of openmc.Material, optional + fill : openmc.Material or openmc.UniverseBase or openmc.Lattice or None or iterable of openmc.Material, optional Indicates what the region of space is filled with region : openmc.Region, optional Region of space that is assigned to the cell. @@ -38,7 +38,7 @@ class Cell(IDManagerMixin): Unique identifier for the cell name : str Name of the cell - fill : openmc.Material or openmc.Universe or openmc.Lattice or None or iterable of openmc.Material + fill : openmc.Material or openmc.UniverseBase or openmc.Lattice or None or iterable of openmc.Material Indicates what the region of space is filled with. If None, the cell is treated as a void. An iterable of materials is used to fill repeated instances of a cell with different materials. @@ -156,7 +156,7 @@ class Cell(IDManagerMixin): def fill_type(self): if isinstance(self.fill, openmc.Material): return 'material' - elif isinstance(self.fill, openmc.Universe): + elif isinstance(self.fill, openmc.UniverseBase): return 'universe' elif isinstance(self.fill, openmc.Lattice): return 'lattice' @@ -278,11 +278,10 @@ class Cell(IDManagerMixin): cv.check_type('cell.fill[i]', f, openmc.Material) elif not isinstance(fill, (openmc.Material, openmc.Lattice, - openmc.Universe)): + openmc.UniverseBase)): msg = ('Unable to set Cell ID="{0}" to use a non-Material or ' 'Universe fill "{1}"'.format(self._id, fill)) raise ValueError(msg) - self._fill = fill # Info about atom content can now be invalid diff --git a/openmc/filter.py b/openmc/filter.py index e0cedd6bf2..e46effa7bd 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -15,7 +15,7 @@ from .cell import Cell from .material import Material from .mixin import IDManagerMixin from .surface import Surface -from .universe import Universe +from .universe import UniverseBase _FILTER_TYPES = ( @@ -408,8 +408,8 @@ class UniverseFilter(WithIDFilter): Parameters ---------- - bins : openmc.Universe, int, or iterable thereof - The Universes to tally. Either openmc.Universe objects or their + bins : openmc.UniverseBase, int, or iterable thereof + The Universes to tally. Either openmc.UniverseBase objects or their Integral ID numbers can be used. filter_id : int Unique identifier for the filter @@ -417,14 +417,14 @@ class UniverseFilter(WithIDFilter): Attributes ---------- bins : Iterable of Integral - openmc.Universe IDs. + openmc.UniverseBase IDs. id : int Unique identifier for the filter num_bins : Integral The number of filter bins """ - expected_type = Universe + expected_type = UniverseBase class MaterialFilter(WithIDFilter): diff --git a/openmc/geometry.py b/openmc/geometry.py index e7d981d7b8..8f7bf05f85 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -14,13 +14,13 @@ class Geometry: Parameters ---------- - root : openmc.Universe or Iterable of openmc.Cell, optional + root : openmc.UniverseBase or Iterable of openmc.Cell, optional Root universe which contains all others, or an iterable of cells that should be used to create a root universe. Attributes ---------- - root_universe : openmc.Universe + root_universe : openmc.UniverseBase Root universe which contains all others bounding_box : 2-tuple of numpy.array Lower-left and upper-right coordinates of an axis-aligned bounding box @@ -32,7 +32,7 @@ class Geometry: self._root_universe = None self._offsets = {} if root is not None: - if isinstance(root, openmc.Universe): + if isinstance(root, openmc.UniverseBase): self.root_universe = root else: univ = openmc.Universe() @@ -50,7 +50,7 @@ class Geometry: @root_universe.setter def root_universe(self, root_universe): - check_type('root universe', root_universe, openmc.Universe) + check_type('root universe', root_universe, openmc.UniverseBase) self._root_universe = root_universe def add_volume_information(self, volume_calc): @@ -159,6 +159,11 @@ class Geometry: for s1, s2 in periodic.items(): surfaces[s1].periodic_surface = surfaces[s2] + # Add any DAGMC universes + for elem in root.findall('dagmc_universe'): + dag_univ = openmc.DAGMCUniverse.from_xml_element(elem) + universes[dag_univ.id] = dag_univ + # Dictionary that maps each universe to a list of cells/lattices that # contain it (needed to determine which universe is the root) child_of = defaultdict(list) diff --git a/openmc/lattice.py b/openmc/lattice.py index 252fbf3274..5112dde05a 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -488,7 +488,7 @@ class RectLattice(Lattice): @Lattice.universes.setter def universes(self, universes): - cv.check_iterable_type('lattice universes', universes, openmc.Universe, + cv.check_iterable_type('lattice universes', universes, openmc.UniverseBase, min_depth=2, max_depth=3) self._universes = np.asarray(universes) diff --git a/openmc/model/model.py b/openmc/model/model.py index 7d6acb281c..0501f03f3a 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -179,8 +179,7 @@ class Model: d.mkdir(parents=True) self.settings.export_to_xml(d) - if not self.settings.dagmc: - self.geometry.export_to_xml(d) + self.geometry.export_to_xml(d) # If a materials collection was specified, export it. Otherwise, look # for all materials in the geometry and use that to automatically build diff --git a/openmc/settings.py b/openmc/settings.py index 615e6365aa..7696aee9fc 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -44,8 +44,6 @@ class Settings: weight assigned to particles that are not killed after Russian roulette. Value of energy should be a float indicating energy in eV below which particle type will be killed. - dagmc : bool - Indicate that a CAD-based DAGMC geometry will be used. delayed_photon_scaling : bool Indicate whether to scale the fission photon yield by (EGP + EGD)/EGP where EGP is the energy release of prompt photons and EGD is the energy @@ -269,8 +267,6 @@ class Settings: self._material_cell_offsets = None self._log_grid_bins = None - self._dagmc = False - self._event_based = None self._max_particles_in_flight = None @@ -434,10 +430,6 @@ class Settings: def log_grid_bins(self): return self._log_grid_bins - @property - def dagmc(self): - return self._dagmc - @property def event_based(self): return self._event_based @@ -633,11 +625,6 @@ class Settings: cv.check_type('photon transport', photon_transport, bool) self._photon_transport = photon_transport - @dagmc.setter - def dagmc(self, dagmc): - cv.check_type('dagmc geometry', dagmc, bool) - self._dagmc = dagmc - @ptables.setter def ptables(self, ptables): cv.check_type('probability tables', ptables, bool) @@ -1125,11 +1112,6 @@ class Settings: elem = ET.SubElement(root, "log_grid_bins") elem.text = str(self._log_grid_bins) - def _create_dagmc_subelement(self, root): - if self._dagmc: - elem = ET.SubElement(root, "dagmc") - elem.text = str(self._dagmc).lower() - def _eigenvalue_from_xml_element(self, root): elem = root.find('eigenvalue') if elem is not None: @@ -1407,11 +1389,6 @@ class Settings: if text is not None: self.log_grid_bins = int(text) - def _dagmc_from_xml_element(self, root): - text = get_text(root, 'dagmc') - if text is not None: - self.dagmc = text in ('true', '1') - def export_to_xml(self, path='settings.xml'): """Export simulation settings to an XML file. @@ -1465,7 +1442,6 @@ class Settings: self._create_max_particles_in_flight_subelement(root_element) self._create_material_cell_offsets_subelement(root_element) self._create_log_grid_bins_subelement(root_element) - self._create_dagmc_subelement(root_element) # Clean the indentation in the file to be user-readable clean_indentation(root_element) @@ -1539,7 +1515,6 @@ class Settings: settings._max_particles_in_flight_from_xml_element(root) settings._material_cell_offsets_from_xml_element(root) settings._log_grid_bins_from_xml_element(root) - settings._dagmc_from_xml_element(root) # TODO: Get volume calculations diff --git a/openmc/summary.py b/openmc/summary.py index 63ed665169..6e64120618 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -123,7 +123,9 @@ class Summary: def _read_surfaces(self): for group in self._f['geometry/surfaces'].values(): surface = openmc.Surface.from_hdf5(group) - self._fast_surfaces[surface.id] = surface + # surface may be None for DAGMC surfaces + if surface: + self._fast_surfaces[surface.id] = surface def _read_cells(self): @@ -176,7 +178,11 @@ class Summary: def _read_universes(self): for group in self._f['geometry/universes'].values(): - universe = openmc.Universe.from_hdf5(group, self._fast_cells) + geom_type = group.get('geom_type') + if geom_type and geom_type[()].decode() == 'dagmc': + universe = openmc.DAGMCUniverse.from_hdf5(group) + else: + universe = openmc.Universe.from_hdf5(group, self._fast_cells) self._fast_universes[universe.id] = universe def _read_lattices(self): diff --git a/openmc/surface.py b/openmc/surface.py index d71e73788e..ac0cb1b0b2 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -432,6 +432,7 @@ class Surface(IDManagerMixin, ABC): kwargs = {} kwargs['surface_id'] = int(elem.get('id')) kwargs['boundary_type'] = elem.get('boundary', 'transmission') + kwargs['name'] = elem.get('name') coeffs = [float(x) for x in elem.get('coeffs').split()] kwargs.update(dict(zip(cls._coeff_keys, coeffs))) @@ -453,13 +454,19 @@ class Surface(IDManagerMixin, ABC): """ + # If this is a DAGMC surface, do nothing for now + geom_type = group.get('geom_type') + if geom_type and geom_type[()].decode() == 'dagmc': + return + surface_id = int(group.name.split('/')[-1].lstrip('surface ')) name = group['name'][()].decode() if 'name' in group else '' - surf_type = group['type'][()].decode() + bc = group['boundary_type'][()].decode() coeffs = group['coefficients'][...] kwargs = {'boundary_type': bc, 'name': name, 'surface_id': surface_id} + surf_type = group['type'][()].decode() cls = _SURFACE_CLASSES[surf_type] return cls(*coeffs, **kwargs) diff --git a/openmc/universe.py b/openmc/universe.py index 2dfbf9cd75..5cfb985793 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -1,18 +1,154 @@ +from abc import ABC, abstractmethod from collections import OrderedDict from collections.abc import Iterable from copy import copy, deepcopy from numbers import Real import random +from xml.etree import ElementTree as ET import numpy as np import openmc import openmc.checkvalue as cv +from ._xml import get_text from .mixin import IDManagerMixin from .plots import _SVG_COLORS -class Universe(IDManagerMixin): +class UniverseBase(ABC, IDManagerMixin): + """A collection of cells that can be repeated. + + Attributes + ---------- + id : int + Unique identifier of the universe + name : str + Name of the universe + """ + + next_id = 1 + used_ids = set() + + def __init__(self, universe_id=None, name=''): + # Initialize Universe class attributes + self.id = universe_id + self.name = name + self._volume = None + self._atoms = {} + + # Keys - Cell IDs + # Values - Cells + self._cells = OrderedDict() + + def __repr__(self): + string = 'Universe\n' + string += '{: <16}=\t{}\n'.format('\tID', self._id) + string += '{: <16}=\t{}\n'.format('\tName', self._name) + return string + + @property + def name(self): + return self._name + + @property + def volume(self): + return self._volume + + @name.setter + def name(self, name): + if name is not None: + cv.check_type('universe name', name, str) + self._name = name + else: + self._name = '' + + @volume.setter + def volume(self, volume): + if volume is not None: + cv.check_type('universe volume', volume, Real) + self._volume = volume + + def add_volume_information(self, volume_calc): + """Add volume information to a universe. + + Parameters + ---------- + volume_calc : openmc.VolumeCalculation + Results from a stochastic volume calculation + + """ + if volume_calc.domain_type == 'universe': + if self.id in volume_calc.volumes: + self._volume = volume_calc.volumes[self.id].n + self._atoms = volume_calc.atoms[self.id] + else: + raise ValueError('No volume information found for this universe.') + else: + raise ValueError('No volume information found for this universe.') + + @abstractmethod + def create_xml_subelement(self, xml_element, memo=None): + """Add the universe xml representation to an incoming xml element + + Parameters + ---------- + xml_element : xml.etree.ElementTree.Element + XML element to be added to + + memo : set or None + A set of object id's representing geometry entities already + written to the xml_element. This parameter is used internally + and should not be specified by users. + + Returns + ------- + None + + """ + + def clone(self, clone_materials=True, clone_regions=True, memo=None): + """Create a copy of this universe with a new unique ID, and clones + all cells within this universe. + + Parameters + ---------- + clone_materials : bool + Whether to create separates copies of the materials filling cells + contained in this universe. + clone_regions : bool + Whether to create separates copies of the regions bounding cells + contained in this universe. + memo : dict or None + A nested dictionary of previously cloned objects. This parameter + is used internally and should not be specified by the user. + + Returns + ------- + clone : openmc.Universe + The clone of this universe + + """ + if memo is None: + memo = {} + + # If no memoize'd clone exists, instantiate one + if self not in memo: + clone = deepcopy(self) + clone.id = None + + # Clone all cells for the universe clone + clone._cells = OrderedDict() + for cell in self._cells.values(): + clone.add_cell(cell.clone(clone_materials, clone_regions, + memo)) + + # Memoize the clone + memo[self] = clone + + return memo[self] + + +class Universe(UniverseBase): """A collection of cells that can be repeated. Parameters @@ -44,42 +180,22 @@ class Universe(IDManagerMixin): """ - next_id = 1 - used_ids = set() - def __init__(self, universe_id=None, name='', cells=None): - # Initialize Cell class attributes - self.id = universe_id - self.name = name - self._volume = None - self._atoms = {} - - # Keys - Cell IDs - # Values - Cells - self._cells = OrderedDict() + super().__init__(universe_id, name) if cells is not None: self.add_cells(cells) def __repr__(self): - string = 'Universe\n' - string += '{: <16}=\t{}\n'.format('\tID', self._id) - string += '{: <16}=\t{}\n'.format('\tName', self._name) + string = super().__repr__() + string += '{: <16}=\t{}\n'.format('\tGeom', 'CSG') string += '{: <16}=\t{}\n'.format('\tCells', list(self._cells.keys())) return string - @property - def name(self): - return self._name - @property def cells(self): return self._cells - @property - def volume(self): - return self._volume - @property def bounding_box(self): regions = [c.region for c in self.cells.values() @@ -90,20 +206,6 @@ class Universe(IDManagerMixin): # Infinite bounding box return openmc.Intersection([]).bounding_box - @name.setter - def name(self, name): - if name is not None: - cv.check_type('universe name', name, str) - self._name = name - else: - self._name = '' - - @volume.setter - def volume(self, volume): - if volume is not None: - cv.check_type('universe volume', volume, Real) - self._volume = volume - @classmethod def from_hdf5(cls, group, cells): """Create universe from HDF5 group @@ -133,24 +235,6 @@ class Universe(IDManagerMixin): return universe - def add_volume_information(self, volume_calc): - """Add volume information to a universe. - - Parameters - ---------- - volume_calc : openmc.VolumeCalculation - Results from a stochastic volume calculation - - """ - if volume_calc.domain_type == 'universe': - if self.id in volume_calc.volumes: - self._volume = volume_calc.volumes[self.id].n - self._atoms = volume_calc.atoms[self.id] - else: - raise ValueError('No volume information found for this universe.') - else: - raise ValueError('No volume information found for this universe.') - def find(self, point): """Find cells/universes/lattices which contain a given point @@ -480,66 +564,7 @@ class Universe(IDManagerMixin): return universes - def clone(self, clone_materials=True, clone_regions=True, memo=None): - """Create a copy of this universe with a new unique ID, and clones - all cells within this universe. - - Parameters - ---------- - clone_materials : bool - Whether to create separates copies of the materials filling cells - contained in this universe. - clone_regions : bool - Whether to create separates copies of the regions bounding cells - contained in this universe. - memo : dict or None - A nested dictionary of previously cloned objects. This parameter - is used internally and should not be specified by the user. - - Returns - ------- - clone : openmc.Universe - The clone of this universe - - """ - - if memo is None: - memo = {} - - # If no nemoize'd clone exists, instantiate one - if self not in memo: - clone = deepcopy(self) - clone.id = None - - # Clone all cells for the universe clone - clone._cells = OrderedDict() - for cell in self._cells.values(): - clone.add_cell(cell.clone(clone_materials, clone_regions, - memo)) - - # Memoize the clone - memo[self] = clone - - return memo[self] - def create_xml_subelement(self, xml_element, memo=None): - """Add the universe xml representation to an incoming xml element - - Parameters - ---------- - xml_element : xml.etree.ElementTree.Element - XML element to be added to - - memo : set or None - A set of object id's representing geometry entities already - written to the xml_element. This parameter is used internally - and should not be specified by users. - - Returns - ------- - None - - """ # Iterate over all Cells for cell in self._cells.values(): @@ -600,3 +625,163 @@ class Universe(IDManagerMixin): cell._num_instances += 1 if not instances_only: cell._paths.append(cell_path) + + +class DAGMCUniverse(UniverseBase): + """A reference to a DAGMC file to be used in the model. + + Parameters + ---------- + filename : str + Path to the DAGMC file used to represent this universe. + universe_id : int, optional + Unique identifier of the universe. If not specified, an identifier will + automatically be assigned. + name : str, optional + Name of the universe. If not specified, the name is the empty string. + auto_geom_ids : bool + Set IDs automatically on initialization (True) or report overlaps + in ID space between CSG and DAGMC (False) + auto_mat_ids : bool + Set IDs automatically on initialization (True) or report overlaps + in ID space between OpenMC and UWUW materials (False) + + Attributes + ---------- + id : int + Unique identifier of the universe + name : str + Name of the universe + filename : str + Path to the DAGMC file used to represent this universe. + auto_geom_ids : bool + Set IDs automatically on initialization (True) or report overlaps + in ID space between CSG and DAGMC (False) + auto_mat_ids : bool + Set IDs automatically on initialization (True) or report overlaps + in ID space between OpenMC and UWUW materials (False) + """ + + def __init__(self, + filename, + universe_id=None, + name='', + auto_geom_ids=False, + auto_mat_ids=False): + super().__init__(universe_id, name) + # Initialize class attributes + self.filename = filename + self.auto_geom_ids = auto_geom_ids + self.auto_mat_ids = auto_mat_ids + + def __repr__(self): + string = super().__repr__() + string += '{: <16}=\t{}\n'.format('\tGeom', 'DAGMC') + string += '{: <16}=\t{}\n'.format('\tFile', self.filename) + return string + + @property + def filename(self): + return self._filename + + @filename.setter + def filename(self, val): + cv.check_type('DAGMC filename', val, str) + self._filename = val + + @property + def auto_geom_ids(self): + return self._auto_geom_ids + + @auto_geom_ids.setter + def auto_geom_ids(self, val): + cv.check_type('DAGMC automatic geometry ids', val, bool) + self._auto_geom_ids = val + + @property + def auto_mat_ids(self): + return self._auto_mat_ids + + @auto_mat_ids.setter + def auto_mat_ids(self, val): + cv.check_type('DAGMC automatic material ids', val, bool) + self._auto_mat_ids = val + + def get_all_cells(self, memo=None): + return OrderedDict() + + def get_all_materials(self, memo=None): + return OrderedDict() + + def create_xml_subelement(self, xml_element, memo=None): + if memo and self in memo: + return + + if memo is not None: + memo.add(self) + + # Set xml element values + dagmc_element = ET.Element('dagmc_universe') + dagmc_element.set('id', str(self.id)) + + if self.auto_geom_ids: + dagmc_element.set('auto_geom_ids', 'true') + if self.auto_mat_ids: + dagmc_element.set('auto_mat_ids', 'true') + dagmc_element.set('filename', self.filename) + xml_element.append(dagmc_element) + + @classmethod + def from_hdf5(cls, group): + """Create DAGMC universe from HDF5 group + + Parameters + ---------- + group : h5py.Group + Group in HDF5 file + + Returns + ------- + openmc.DAGMCUniverse + DAGMCUniverse instance + + """ + id = int(group.name.split('/')[-1].lstrip('universe ')) + fname = group['filename'][()].decode() + name = group['name'][()].decode() if 'name' in group else None + + out = cls(fname, universe_id=id, name=name) + + out.auto_geom_ids = bool(group.attrs['auto_geom_ids']) + out.auto_mat_ids = bool(group.attrs['auto_mat_ids']) + + return out + + @classmethod + def from_xml_element(cls, elem): + """Generate DAGMC universe from XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + `` element + + Returns + ------- + openmc.DAGMCUniverse + DAGMCUniverse instance + + """ + id = int(get_text(elem, 'id')) + fname = get_text(elem, 'filename') + + out = cls(fname, universe_id=id) + + name = get_text(elem, 'name') + if name is not None: + out.name = name + + out.auto_geom_ids = bool(elem.get('auto_geom_ids')) + out.auto_mat_ids = bool(elem.get('auto_mat_ids')) + + return out diff --git a/src/cell.cpp b/src/cell.cpp index 6b88a67803..4ae5177fa0 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -22,7 +22,6 @@ #include "openmc/material.h" #include "openmc/nuclide.h" #include "openmc/settings.h" -#include "openmc/surface.h" #include "openmc/xml_interface.h" namespace openmc { @@ -194,6 +193,9 @@ Universe::to_hdf5(hid_t universes_group) const // Create a group for this universe. auto group = create_group(universes_group, fmt::format("universe {}", id_)); + // Write the geometry representation type. + write_string(group, "geom_type", "csg", false); + // Write the contained cells. if (cells_.size() > 0) { vector cell_ids; @@ -204,6 +206,31 @@ Universe::to_hdf5(hid_t universes_group) const close_group(group); } +bool +Universe::find_cell(Particle& p) const { + const auto& cells { + !partitioner_ + ? cells_ + : partitioner_->get_cells(p.r_local(), p.u_local()) + }; + + for (auto it = cells.begin(); it != cells.end(); it++) { + int32_t i_cell = *it; + int32_t i_univ = p.coord(p.n_coord()-1).universe; + if (model::cells[i_cell]->universe_ != i_univ) continue; + + // Check if this cell contains the particle; + Position r {p.r_local()}; + Direction u {p.u_local()}; + auto surf = p.surface(); + if (model::cells[i_cell]->contains(r, u, surf)) { + p.coord(p.n_coord()-1).cell = i_cell; + return true; + } + } + return false; +} + BoundingBox Universe::bounding_box() const { BoundingBox bbox = {INFTY, -INFTY, INFTY, -INFTY, INFTY, -INFTY}; if (cells_.size() == 0) { @@ -282,14 +309,78 @@ Cell::set_temperature(double T, int32_t instance, bool set_contained) } } +void +Cell::to_hdf5(hid_t cell_group) const { + + // Create a group for this cell. + auto group = create_group(cell_group, fmt::format("cell {}", id_)); + + if (!name_.empty()) { + write_string(group, "name", name_, false); + } + + write_dataset(group, "universe", model::universes[universe_]->id_); + + to_hdf5_inner(group); + + // Write fill information. + if (type_ == Fill::MATERIAL) { + write_dataset(group, "fill_type", "material"); + std::vector mat_ids; + for (auto i_mat : material_) { + if (i_mat != MATERIAL_VOID) { + mat_ids.push_back(model::materials[i_mat]->id_); + } else { + mat_ids.push_back(MATERIAL_VOID); + } + } + if (mat_ids.size() == 1) { + write_dataset(group, "material", mat_ids[0]); + } else { + write_dataset(group, "material", mat_ids); + } + + std::vector temps; + for (auto sqrtkT_val : sqrtkT_) + temps.push_back(sqrtkT_val * sqrtkT_val / K_BOLTZMANN); + write_dataset(group, "temperature", temps); + + } else if (type_ == Fill::UNIVERSE) { + write_dataset(group, "fill_type", "universe"); + write_dataset(group, "fill", model::universes[fill_]->id_); + if (translation_ != Position(0, 0, 0)) { + write_dataset(group, "translation", translation_); + } + if (!rotation_.empty()) { + if (rotation_.size() == 12) { + std::array rot {rotation_[9], rotation_[10], rotation_[11]}; + write_dataset(group, "rotation", rot); + } else { + write_dataset(group, "rotation", rotation_); + } + } + + } else if (type_ == Fill::LATTICE) { + write_dataset(group, "fill_type", "lattice"); + write_dataset(group, "lattice", model::lattices[fill_]->id_); + } + + close_group(group); +} + //============================================================================== // CSGCell implementation //============================================================================== -CSGCell::CSGCell() {} // empty constructor +// default constructor +CSGCell::CSGCell() { + geom_type_ = GeometryType::CSG; +} CSGCell::CSGCell(pugi::xml_node cell_node) { + geom_type_ = GeometryType::CSG; + if (check_for_node(cell_node, "id")) { id_ = std::stoi(get_node_value(cell_node, "id")); } else { @@ -526,16 +617,10 @@ CSGCell::distance(Position r, Direction u, int32_t on_surface, Particle* p) cons //============================================================================== void -CSGCell::to_hdf5(hid_t cell_group) const +CSGCell::to_hdf5_inner(hid_t group_id) const { - // Create a group for this cell. - auto group = create_group(cell_group, fmt::format("cell {}", id_)); - if (!name_.empty()) { - write_string(group, "name", name_, false); - } - - write_dataset(group, "universe", model::universes[universe_]->id_); + write_string(group_id, "geom_type", "csg", false); // Write the region specification. if (!region_.empty()) { @@ -556,52 +641,9 @@ CSGCell::to_hdf5(hid_t cell_group) const region_spec << " " << ((token > 0) ? surf_id : -surf_id); } } - write_string(group, "region", region_spec.str(), false); + write_string(group_id, "region", region_spec.str(), false); } - // Write fill information. - if (type_ == Fill::MATERIAL) { - write_dataset(group, "fill_type", "material"); - vector mat_ids; - for (auto i_mat : material_) { - if (i_mat != MATERIAL_VOID) { - mat_ids.push_back(model::materials[i_mat]->id_); - } else { - mat_ids.push_back(MATERIAL_VOID); - } - } - if (mat_ids.size() == 1) { - write_dataset(group, "material", mat_ids[0]); - } else { - write_dataset(group, "material", mat_ids); - } - - vector temps; - for (auto sqrtkT_val : sqrtkT_) - temps.push_back(sqrtkT_val * sqrtkT_val / K_BOLTZMANN); - write_dataset(group, "temperature", temps); - - } else if (type_ == Fill::UNIVERSE) { - write_dataset(group, "fill_type", "universe"); - write_dataset(group, "fill", model::universes[fill_]->id_); - if (translation_ != Position(0, 0, 0)) { - write_dataset(group, "translation", translation_); - } - if (!rotation_.empty()) { - if (rotation_.size() == 12) { - array rot {rotation_[9], rotation_[10], rotation_[11]}; - write_dataset(group, "rotation", rot); - } else { - write_dataset(group, "rotation", rotation_); - } - } - - } else if (type_ == Fill::LATTICE) { - write_dataset(group, "fill_type", "lattice"); - write_dataset(group, "lattice", model::lattices[fill_]->id_); - } - - close_group(group); } BoundingBox CSGCell::bounding_box_simple() const { @@ -776,70 +818,6 @@ CSGCell::contains_complex(Position r, Direction u, int32_t on_surface) const } } -//============================================================================== -// DAGMC Cell implementation -//============================================================================== -#ifdef DAGMC -DAGCell::DAGCell() : Cell{} { simple_ = true; }; - -std::pair -DAGCell::distance(Position r, Direction u, int32_t on_surface, Particle* p) const -{ - Expects(p); - // if we've changed direction or we're not on a surface, - // reset the history and update last direction - if (u != p->last_dir() || on_surface == 0) { - p->history().reset(); - p->last_dir() = u; - } - - moab::ErrorCode rval; - moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); - moab::EntityHandle hit_surf; - double dist; - double pnt[3] = {r.x, r.y, r.z}; - double dir[3] = {u.x, u.y, u.z}; - rval = dagmc_ptr_->ray_fire(vol, pnt, dir, hit_surf, dist, &p->history()); - MB_CHK_ERR_CONT(rval); - int surf_idx; - if (hit_surf != 0) { - surf_idx = dagmc_ptr_->index_by_handle(hit_surf); - } else { - // indicate that particle is lost - surf_idx = -1; - dist = INFINITY; - } - - return {dist, surf_idx}; -} - -bool DAGCell::contains(Position r, Direction u, int32_t on_surface) const -{ - moab::ErrorCode rval; - moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); - - int result = 0; - double pnt[3] = {r.x, r.y, r.z}; - double dir[3] = {u.x, u.y, u.z}; - rval = dagmc_ptr_->point_in_volume(vol, pnt, result, dir); - MB_CHK_ERR_CONT(rval); - return result; -} - -void DAGCell::to_hdf5(hid_t group_id) const { return; } - -BoundingBox DAGCell::bounding_box() const -{ - moab::ErrorCode rval; - moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); - double min[3], max[3]; - rval = dagmc_ptr_->getobb(vol, min, max); - MB_CHK_ERR_CONT(rval); - return {min[0], max[0], min[1], max[1], min[2], max[2]}; -} - -#endif - //============================================================================== // UniversePartitioner implementation //============================================================================== @@ -992,9 +970,6 @@ void read_cells(pugi::xml_node node) // Count the number of cells. int n_cells = 0; for (pugi::xml_node cell_node: node.children("cell")) {n_cells++;} - if (n_cells == 0) { - fatal_error("No cells found in geometry.xml!"); - } // Loop over XML cell elements and populate the array. model::cells.reserve(n_cells); @@ -1013,6 +988,8 @@ void read_cells(pugi::xml_node node) } } + read_dagmc_universes(node); + // Populate the Universe vector and map. for (int i = 0; i < model::cells.size(); i++) { int32_t uid = model::cells[i]->universe_; @@ -1032,6 +1009,10 @@ void read_cells(pugi::xml_node node) if (settings::check_overlaps) { model::overlap_check_count.resize(model::cells.size(), 0); } + + if (model::cells.size() == 0) { + fatal_error("No cells were found in the geometry.xml file"); + } } //============================================================================== @@ -1290,21 +1271,6 @@ openmc_extend_cells(int32_t n, int32_t* index_start, int32_t* index_end) return 0; } -#ifdef DAGMC -int32_t next_cell(DAGCell* cur_cell, DAGSurface* surf_xed) -{ - moab::EntityHandle surf = - surf_xed->dagmc_ptr_->entity_by_index(2, surf_xed->dag_index_); - moab::EntityHandle vol = - cur_cell->dagmc_ptr_->entity_by_index(3, cur_cell->dag_index_); - - moab::EntityHandle new_vol; - cur_cell->dagmc_ptr_->next_vol(surf, vol, new_vol); - - return cur_cell->dagmc_ptr_->index_by_handle(new_vol); -} -#endif - extern "C" int cells_size() { return model::cells.size(); } } // namespace openmc diff --git a/src/cross_sections.cpp b/src/cross_sections.cpp index ea8d0cfbba..8124bd7947 100644 --- a/src/cross_sections.cpp +++ b/src/cross_sections.cpp @@ -3,9 +3,6 @@ #include "openmc/capi.h" #include "openmc/constants.h" #include "openmc/container_util.h" -#ifdef DAGMC -#include "openmc/dagmc.h" -#endif #include "openmc/error.h" #include "openmc/geometry_aux.h" #include "openmc/file_utils.h" @@ -96,27 +93,12 @@ void read_cross_sections_xml() { pugi::xml_document doc; std::string filename = settings::path_input + "materials.xml"; -#ifdef DAGMC - std::string s; - bool found_uwuw_mats = false; - if (settings::dagmc) { - found_uwuw_mats = get_uwuw_materials_xml(s); - } - - if (found_uwuw_mats) { - // if we found uwuw materials, load those - doc.load_file(s.c_str()); - } else { -#endif // Check if materials.xml exists if (!file_exists(filename)) { fatal_error("Material XML file '" + filename + "' does not exist."); } // Parse materials.xml file doc.load_file(filename.c_str()); -#ifdef DAGMC - } -#endif auto root = doc.document_element(); diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 0c2768834a..630f665fae 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -1,16 +1,15 @@ #include "openmc/dagmc.h" -#include "openmc/cell.h" #include "openmc/constants.h" #include "openmc/container_util.h" #include "openmc/error.h" #include "openmc/file_utils.h" #include "openmc/geometry.h" #include "openmc/geometry_aux.h" +#include "openmc/hdf5_interface.h" #include "openmc/material.h" #include "openmc/string_utils.h" #include "openmc/settings.h" -#include "openmc/surface.h" #ifdef DAGMC #include "uwuw.hpp" @@ -37,189 +36,135 @@ const bool DAGMC_ENABLED = false; namespace openmc { -const std::string DAGMC_FILENAME = "dagmc.h5m"; +//============================================================================== +// DAGMC Universe implementation +//============================================================================== -namespace model { - -moab::DagMC* DAG; - -} // namespace model - - -std::string dagmc_file() { - std::string filename = settings::path_input + DAGMC_FILENAME; - if (!file_exists(filename)) { - fatal_error("Geometry DAGMC file '" + filename + "' does not exist!"); +DAGUniverse::DAGUniverse(pugi::xml_node node) { + if (check_for_node(node, "id")) { + id_ = std::stoi(get_node_value(node, "id")); + } else { + fatal_error("Must specify the id of the DAGMC universe"); } - return filename; + + if (check_for_node(node, "filename")) { + filename_ = get_node_value(node, "filename"); + } else { + fatal_error("Must specify a file for the DAGMC universe"); + } + + adjust_geometry_ids_ = false; + if (check_for_node(node, "auto_geom_ids")) { + adjust_geometry_ids_ = get_node_value_bool(node, "auto_geom_ids"); + } + + adjust_material_ids_ = false; + if (check_for_node(node, "auto_mat_ids")) { + adjust_material_ids_ = get_node_value_bool(node, "auto_mat_ids"); + } + + initialize(); } -bool get_uwuw_materials_xml(std::string& s) { - std::string filename = dagmc_file(); - UWUW uwuw(filename.c_str()); - - std::stringstream ss; - bool uwuw_mats_present = false; - if (uwuw.material_library.size() != 0) { - uwuw_mats_present = true; - // write header - ss << "\n"; - ss << "\n"; - const auto& mat_lib = uwuw.material_library; - // write materials - for (auto mat : mat_lib) { ss << mat.second->openmc("atom"); } - // write footer - ss << ""; - s = ss.str(); +DAGUniverse::DAGUniverse(const std::string& filename, + bool auto_geom_ids, + bool auto_mat_ids) +: filename_(filename), adjust_geometry_ids_(auto_geom_ids), adjust_material_ids_(auto_mat_ids) { + // determine the next universe id + int32_t next_univ_id = 0; + for (const auto& u : model::universes) { + if (u->id_ > next_univ_id) next_univ_id = u->id_; } + next_univ_id++; - return uwuw_mats_present; + // set the universe id + id_ = next_univ_id; + + initialize(); } -bool read_uwuw_materials(pugi::xml_document& doc) { - std::string s; - bool found_uwuw_mats = get_uwuw_materials_xml(s); - if (found_uwuw_mats) { - pugi::xml_parse_result result = doc.load_string(s.c_str()); - if (!result) { - throw std::runtime_error{"Error reading UWUW materials"}; - } - } - return found_uwuw_mats; -} +void +DAGUniverse::initialize() { + geom_type() = GeometryType::DAG; -bool write_uwuw_materials_xml() { - std::string s; - bool found_uwuw_mats = get_uwuw_materials_xml(s); - // if there is a material library in the file - if (found_uwuw_mats) { - // write a material.xml file - std::ofstream mats_xml("materials.xml"); - mats_xml << s; - mats_xml.close(); + // determine the next cell id + int32_t next_cell_id = 0; + for (const auto& c : model::cells) { + if (c->id_ > next_cell_id) next_cell_id = c->id_; } + cell_idx_offset_ = model::cells.size(); + next_cell_id++; - return found_uwuw_mats; -} - -void legacy_assign_material(std::string mat_string, DAGCell* c) -{ - bool mat_found_by_name = false; - // attempt to find a material with a matching name - to_lower(mat_string); - for (const auto& m : model::materials) { - std::string m_name = m->name(); - to_lower(m_name); - if (mat_string == m_name) { - // assign the material with that name - if (!mat_found_by_name) { - mat_found_by_name = true; - c->material_.push_back(m->id_); - // report error if more than one material is found - } else { - fatal_error(fmt::format( - "More than one material found with name {}. Please ensure materials " - "have unique names if using this property to assign materials.", - mat_string)); - } - } + // determine the next surface id + int32_t next_surf_id = 0; + for (const auto& s : model::surfaces) { + if (s->id_ > next_surf_id) next_surf_id = s->id_; } + surf_idx_offset_ = model::surfaces.size(); + next_surf_id++; - // if no material was set using a name, assign by id - if (!mat_found_by_name) { - try { - auto id = std::stoi(mat_string); - c->material_.emplace_back(id); - } catch (const std::invalid_argument&) { - fatal_error(fmt::format( - "No material {} found for volume (cell) {}", mat_string, c->id_)); - } - } - - if (settings::verbosity >= 10) { - const auto& m = model::materials[model::material_map.at(c->material_[0])]; - std::stringstream msg; - msg << "DAGMC material " << mat_string << " was assigned"; - if (mat_found_by_name) { - msg << " using material name: " << m->name_; - } else { - msg << " using material id: " << m->id_; - } - write_message(msg.str(), 10); - } -} - -void load_dagmc_geometry() -{ - if (!model::DAG) { - model::DAG = new moab::DagMC(); - } + // create a new DAGMC instance + dagmc_instance_ = std::make_shared(); // --- Materials --- - // create uwuw instance - auto filename = dagmc_file(); - UWUW uwuw(filename.c_str()); + // read any UWUW materials from the file + read_uwuw_materials(); // check for uwuw material definitions - bool using_uwuw = !uwuw.material_library.empty(); + bool using_uwuw = uses_uwuw(); // notify user if UWUW materials are going to be used if (using_uwuw) { write_message("Found UWUW Materials in the DAGMC geometry file.", 6); } - int32_t dagmc_univ_id = 0; // universe is always 0 for DAGMC runs - // load the DAGMC geometry - moab::ErrorCode rval = model::DAG->load_file(filename.c_str()); + filename_ = settings::path_input + filename_; + if (!file_exists(filename_)) { + fatal_error("Geometry DAGMC file '" + filename_ + "' does not exist!"); + } + moab::ErrorCode rval = dagmc_instance_->load_file(filename_.c_str()); MB_CHK_ERR_CONT(rval); // initialize acceleration data structures - rval = model::DAG->init_OBBTree(); + rval = dagmc_instance_->init_OBBTree(); MB_CHK_ERR_CONT(rval); // parse model metadata - dagmcMetaData DMD(model::DAG, false, false); + dagmcMetaData DMD(dagmc_instance_.get(), false, false); DMD.load_property_data(); - vector keywords {"temp"}; + std::vector keywords {"temp"}; std::map dum; std::string delimiters = ":/"; - rval = model::DAG->parse_properties(keywords, dum, delimiters.c_str()); + rval = dagmc_instance_->parse_properties(keywords, dum, delimiters.c_str()); MB_CHK_ERR_CONT(rval); // --- Cells (Volumes) --- // initialize cell objects - int n_cells = model::DAG->num_entities(3); + int n_cells = dagmc_instance_->num_entities(3); moab::EntityHandle graveyard = 0; for (int i = 0; i < n_cells; i++) { - moab::EntityHandle vol_handle = model::DAG->entity_by_index(3, i+1); + moab::EntityHandle vol_handle = dagmc_instance_->entity_by_index(3, i + 1); // set cell ids using global IDs - DAGCell* c = new DAGCell(); - c->dag_index_ = i+1; - c->id_ = model::DAG->id_by_index(3, c->dag_index_); - c->dagmc_ptr_ = model::DAG; - c->universe_ = dagmc_univ_id; // set to zero for now + auto c = std::make_unique(dagmc_instance_, i + 1); + c->id_ = adjust_geometry_ids_ ? next_cell_id++ : dagmc_instance_->id_by_index(3, c->dag_index()); + c->universe_ = this->id_; c->fill_ = C_NONE; // no fill, single universe - model::cells.emplace_back(c); - model::cell_map[c->id_] = i; - - // Populate the Universe vector and dict - auto it = model::universe_map.find(dagmc_univ_id); - if (it == model::universe_map.end()) { - model::universes.push_back(make_unique()); - model::universes.back()->id_ = dagmc_univ_id; - model::universes.back()->cells_.push_back(i); - model::universe_map[dagmc_univ_id] = model::universes.size() - 1; + auto in_map = model::cell_map.find(c->id_); + if (in_map == model::cell_map.end()) { + model::cell_map[c->id_] = model::cells.size(); } else { - model::universes[it->second]->cells_.push_back(i); + warning(fmt::format("DAGMC Cell IDs: {}", dagmc_ids_for_dim(3))); + fatal_error(fmt::format("Cell ID {} exists in both DAGMC Universe {} " + "and the CSG geometry.", c->id_, this->id_)); } - // MATERIALS + // --- Materials --- // determine volume material assignment std::string mat_str = DMD.get_volume_property("material", vol_handle); @@ -241,12 +186,12 @@ void load_dagmc_geometry() if (using_uwuw) { // lookup material in uwuw if present std::string uwuw_mat = DMD.volume_material_property_data_eh[vol_handle]; - if (uwuw.material_library.count(uwuw_mat) != 0) { + if (uwuw_->material_library.count(uwuw_mat) != 0) { // Note: material numbers are set by UWUW - int mat_number = uwuw.material_library.get_material(uwuw_mat).metadata["mat_number"].asInt(); + int mat_number = uwuw_->material_library.get_material(uwuw_mat).metadata["mat_number"].asInt(); c->material_.push_back(mat_number); } else { - fatal_error(fmt::format("Material with value {} not found in the " + fatal_error(fmt::format("Material with value '{}' not found in the " "UWUW material library", mat_str)); } } else { @@ -258,19 +203,25 @@ void load_dagmc_geometry() std::string temp_value; // no temperature if void - if (c->material_[0] == MATERIAL_VOID) continue; + if (c->material_[0] == MATERIAL_VOID) { + model::cells.emplace_back(std::move(c)); + continue; + } // assign cell temperature const auto& mat = model::materials[model::material_map.at(c->material_[0])]; - if (model::DAG->has_prop(vol_handle, "temp")) { - rval = model::DAG->prop_value(vol_handle, "temp", temp_value); + if (dagmc_instance_->has_prop(vol_handle, "temp")) { + rval = dagmc_instance_->prop_value(vol_handle, "temp", temp_value); MB_CHK_ERR_CONT(rval); double temp = std::stod(temp_value); c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * temp)); - } else { + } else if (mat->temperature() > 0.0) { c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * mat->temperature())); + } else { + c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * settings::temperature_default)); } + model::cells.emplace_back(std::move(c)); } // allocate the cell overlap count if necessary @@ -278,40 +229,28 @@ void load_dagmc_geometry() model::overlap_check_count.resize(model::cells.size(), 0); } - if (!graveyard) { - warning("No graveyard volume found in the DagMC model." - "This may result in lost particles and rapid simulation failure."); - } + has_graveyard_ = graveyard; // --- Surfaces --- // initialize surface objects - int n_surfaces = model::DAG->num_entities(2); - + int n_surfaces = dagmc_instance_->num_entities(2); for (int i = 0; i < n_surfaces; i++) { - moab::EntityHandle surf_handle = model::DAG->entity_by_index(2, i+1); + moab::EntityHandle surf_handle = dagmc_instance_->entity_by_index(2, i+1); // set cell ids using global IDs - DAGSurface* s = new DAGSurface(); - s->dag_index_ = i+1; - s->id_ = model::DAG->id_by_index(2, s->dag_index_); - s->dagmc_ptr_ = model::DAG; - - if (contains(settings::source_write_surf_id, s->id_)) { - s->surf_source_ = true; - } + auto s = std::make_unique(dagmc_instance_, i+1); + s->id_ = adjust_geometry_ids_ ? next_surf_id++ : dagmc_instance_->id_by_index(2, i+1); // set BCs std::string bc_value = DMD.get_surface_property("boundary", surf_handle); to_lower(bc_value); if (bc_value.empty() || bc_value == "transmit" || bc_value == "transmission") { - // Leave the bc_ a nullptr + // set to transmission by default (nullptr) } else if (bc_value == "vacuum") { s->bc_ = std::make_shared(); } else if (bc_value == "reflective" || bc_value == "reflect" || bc_value == "reflecting") { s->bc_ = std::make_shared(); - } else if (bc_value == "white") { - fatal_error("White boundary condition not supported in DAGMC."); } else if (bc_value == "periodic") { fatal_error("Periodic boundary condition not supported in DAGMC."); } else { @@ -321,7 +260,7 @@ void load_dagmc_geometry() // graveyard check moab::Range parent_vols; - rval = model::DAG->moab_instance()->get_parent_meshsets(surf_handle, parent_vols); + rval = dagmc_instance_->moab_instance()->get_parent_meshsets(surf_handle, parent_vols); MB_CHK_ERR_CONT(rval); // if this surface belongs to the graveyard @@ -331,26 +270,417 @@ void load_dagmc_geometry() } // add to global array and map - model::surfaces.emplace_back(s); - model::surface_map[s->id_] = i; + + auto in_map = model::surface_map.find(s->id_); + if (in_map == model::surface_map.end()) { + model::surface_map[s->id_] = model::surfaces.size(); + } else { + warning(fmt::format("DAGMC Surface IDs: {}", dagmc_ids_for_dim(2))); + fatal_error(fmt::format("Surface ID {} exists in both Universe {} " + "and the CSG geometry.", s->id_, this->id_)); + } + + model::surfaces.emplace_back(std::move(s)); + } // end surface loop +} + +std::string +DAGUniverse::dagmc_ids_for_dim(int dim) const +{ + // generate a vector of ids + std::vector id_vec; + int n_ents = dagmc_instance_->num_entities(dim); + for (int i = 1; i <= n_ents; i++) { + id_vec.push_back(dagmc_instance_->id_by_index(dim, i)); } - return; + // sort the vector of ids + std::sort(id_vec.begin(), id_vec.end()); + + // generate a string representation of the ID range(s) + std::stringstream out; + + int i = 0; + int start_id = id_vec[0]; // initialize with first ID + int stop_id; + // loop over all cells in the universe + while (i < n_ents) { + + stop_id = id_vec[i]; + + // if the next ID is not in this contiguous set of IDS, + // figure out how to write the string representing this set + if (id_vec[i + 1] > stop_id + 1) { + + if (start_id != stop_id) { + // there are several IDs in a row, print condensed version (i.e. 1-10, 12-20) + out << start_id << "-" << stop_id; + } else { + // only one ID in this contiguous block (i.e. 3, 5, 7, 9) + out << start_id; + } + // insert a comma as long as we aren't in the last ID set + if (i < n_ents - 1) { out << ", "; } + + // if we are at the end of a set, set the start ID to the first value + // in the next set. + start_id = id_vec[++i]; + } + + i++; + } + + return out.str(); } -void read_geometry_dagmc() +int32_t +DAGUniverse::implicit_complement_idx() const { + moab::EntityHandle ic; + moab::ErrorCode rval = dagmc_instance_->geom_tool()->get_implicit_complement(ic); + MB_CHK_SET_ERR_CONT(rval, "Failed to get implicit complement"); + // off-by-one: DAGMC indices start at one + return cell_idx_offset_ + dagmc_instance_->index_by_handle(ic) - 1; +} + +bool +DAGUniverse::find_cell(Particle &p) const { + // if the particle isn't in any of the other DagMC + // cells, place it in the implicit complement + bool found = Universe::find_cell(p); + if (!found && model::universe_map[this->id_] != model::root_universe) { + p.coord(p.n_coord() - 1).cell = implicit_complement_idx(); + found = true; + } + return found; +} + +void +DAGUniverse::to_hdf5(hid_t universes_group) const { + // Create a group for this universe. + auto group = create_group(universes_group, fmt::format("universe {}", id_)); + + // Write the geometry representation type. + write_string(group, "geom_type", "dagmc", false); + + // Write other properties of the DAGMC Universe + write_string(group, "filename", filename_, false); + write_attribute(group, "auto_geom_ids", static_cast(adjust_geometry_ids_)); + write_attribute(group, "auto_mat_ids", static_cast(adjust_material_ids_)); + + close_group(group); +} + +bool +DAGUniverse::uses_uwuw() const { - write_message("Reading DAGMC geometry...", 5); - load_dagmc_geometry(); - - model::root_universe = find_root_universe(); + return !uwuw_->material_library.empty(); } -void free_memory_dagmc() +std::string +DAGUniverse::get_uwuw_materials_xml() const { - delete model::DAG; + if (!uses_uwuw()) { + throw std::runtime_error("This DAGMC Universe does not use UWUW materials"); + } + + std::stringstream ss; + // write header + ss << "\n"; + ss << "\n"; + const auto& mat_lib = uwuw_->material_library; + // write materials + for (auto mat : mat_lib) { ss << mat.second->openmc("atom"); } + // write footer + ss << ""; + + return ss.str(); +} + +void +DAGUniverse::write_uwuw_materials_xml(const std::string& outfile) const +{ + if (!uses_uwuw()) { + throw std::runtime_error("This DAGMC universe does not use UWUW materials."); + } + + std::string xml_str = get_uwuw_materials_xml(); + // if there is a material library in the file + std::ofstream mats_xml(outfile); + mats_xml << xml_str; + mats_xml.close(); +} + +void +DAGUniverse::legacy_assign_material(std::string mat_string, + std::unique_ptr& c) const +{ + bool mat_found_by_name = false; + // attempt to find a material with a matching name + to_lower(mat_string); + for (const auto& m : model::materials) { + std::string m_name = m->name(); + to_lower(m_name); + if (mat_string == m_name) { + // assign the material with that name + if (!mat_found_by_name) { + mat_found_by_name = true; + c->material_.push_back(m->id_); + // report error if more than one material is found + } else { + fatal_error(fmt::format( + "More than one material found with name '{}'. Please ensure materials " + "have unique names if using this property to assign materials.", + mat_string)); + } + } + } + + // if no material was set using a name, assign by id + if (!mat_found_by_name) { + try { + auto id = std::stoi(mat_string); + c->material_.emplace_back(id); + } catch (const std::invalid_argument&) { + fatal_error(fmt::format( + "No material '{}' found for volume (cell) {}", mat_string, c->id_)); + } + } + + if (settings::verbosity >= 10) { + const auto& m = model::materials[model::material_map.at(c->material_[0])]; + std::stringstream msg; + msg << "DAGMC material " << mat_string << " was assigned"; + if (mat_found_by_name) { + msg << " using material name: " << m->name_; + } else { + msg << " using material id: " << m->id_; + } + write_message(msg.str(), 10); + } +} + +void +DAGUniverse::read_uwuw_materials() { + + int32_t next_material_id = 0; + for (const auto& m : model::materials) { + next_material_id = std::max(m->id_, next_material_id); + } + next_material_id++; + + uwuw_ = std::make_shared(filename_.c_str()); + const auto& mat_lib = uwuw_->material_library; + if (mat_lib.size() == 0) return; + + // if we're using automatic IDs, update the UWUW material metadata + if (adjust_material_ids_) { + for (auto& mat : uwuw_->material_library) { + mat.second->metadata["mat_number"] = next_material_id++; + } + } + + std::stringstream ss; + ss << "\n"; + ss << "\n"; + for (auto mat : mat_lib) { ss << mat.second->openmc("atom"); } + ss << ""; + std::string mat_xml_string = ss.str(); + + // create a pugi XML document from this string + pugi::xml_document doc; + auto result = doc.load_string(mat_xml_string.c_str()); + if (!result) { + fatal_error("Error processing XML created using DAGMC UWUW materials."); + } + pugi::xml_node root = doc.document_element(); + for (pugi::xml_node material_node : root.children("material")) { + model::materials.push_back(std::make_unique(material_node)); + } +} + +//============================================================================== +// DAGMC Cell implementation +//============================================================================== + +DAGCell::DAGCell(std::shared_ptr dag_ptr, int32_t dag_idx) + : Cell{}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx) { + geom_type_ = GeometryType::DAG; + simple_ = true; +}; + +std::pair +DAGCell::distance(Position r, Direction u, int32_t on_surface, Particle* p) const +{ + Expects(p); + // if we've changed direction or we're not on a surface, + // reset the history and update last direction + if (u != p->last_dir()) { p->last_dir() = u; p->history().reset(); } + if (on_surface == 0) { p->history().reset(); } + + const auto& univ = model::universes[p->coord(p->n_coord() - 1).universe]; + + DAGUniverse* dag_univ = static_cast(univ.get()); + if (!dag_univ) fatal_error("DAGMC call made for particle in a non-DAGMC universe"); + + moab::ErrorCode rval; + moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); + moab::EntityHandle hit_surf; + double dist; + double pnt[3] = {r.x, r.y, r.z}; + double dir[3] = {u.x, u.y, u.z}; + rval = dagmc_ptr_->ray_fire(vol, pnt, dir, hit_surf, dist, &p->history()); + MB_CHK_ERR_CONT(rval); + int surf_idx; + if (hit_surf != 0) { + surf_idx = dag_univ->surf_idx_offset_ + dagmc_ptr_->index_by_handle(hit_surf); + } else { + // indicate that particle is lost + surf_idx = -1; + dist = INFINITY; + if (!dagmc_ptr_->is_implicit_complement(vol) || model::universe_map[dag_univ->id_] == model::root_universe) { + p->mark_as_lost(fmt::format("No intersection found with DAGMC cell {}", id_)); + } + } + + return {dist, surf_idx}; +} + +bool +DAGCell::contains(Position r, Direction u, int32_t on_surface) const +{ + moab::ErrorCode rval; + moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); + + int result = 0; + double pnt[3] = {r.x, r.y, r.z}; + double dir[3] = {u.x, u.y, u.z}; + rval = dagmc_ptr_->point_in_volume(vol, pnt, result, dir); + MB_CHK_ERR_CONT(rval); + return result; +} + +void +DAGCell::to_hdf5_inner(hid_t group_id) const { + write_string(group_id, "geom_type", "dagmc", false); + +} + +BoundingBox +DAGCell::bounding_box() const +{ + moab::ErrorCode rval; + moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); + double min[3], max[3]; + rval = dagmc_ptr_->getobb(vol, min, max); + MB_CHK_ERR_CONT(rval); + return {min[0], max[0], min[1], max[1], min[2], max[2]}; +} + +//============================================================================== +// DAGSurface implementation +//============================================================================== + +DAGSurface::DAGSurface(std::shared_ptr dag_ptr, int32_t dag_idx) +: Surface{}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx) +{ + geom_type_ = GeometryType::DAG; +} // empty constructor + +double +DAGSurface::evaluate(Position r) const +{ + return 0.0; +} + +double +DAGSurface::distance(Position r, Direction u, bool coincident) const +{ + moab::ErrorCode rval; + moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); + moab::EntityHandle hit_surf; + double dist; + double pnt[3] = {r.x, r.y, r.z}; + double dir[3] = {u.x, u.y, u.z}; + rval = dagmc_ptr_->ray_fire(surf, pnt, dir, hit_surf, dist, NULL, 0, 0); + MB_CHK_ERR_CONT(rval); + if (dist < 0.0) dist = INFTY; + return dist; +} + +Direction +DAGSurface::normal(Position r) const +{ + moab::ErrorCode rval; + moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); + double pnt[3] = {r.x, r.y, r.z}; + double dir[3]; + rval = dagmc_ptr_->get_angle(surf, pnt, dir); + MB_CHK_ERR_CONT(rval); + return dir; +} + +Direction +DAGSurface::reflect(Position r, Direction u, Particle* p) const +{ + Expects(p); + p->history().reset_to_last_intersection(); + moab::ErrorCode rval; + moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); + double pnt[3] = {r.x, r.y, r.z}; + double dir[3]; + rval = dagmc_ptr_->get_angle(surf, pnt, dir, &p->history()); + MB_CHK_ERR_CONT(rval); + p->last_dir() = u.reflect(dir); + return p->last_dir(); +} + +//============================================================================== +// Non-member functions +//============================================================================== + +void read_dagmc_universes(pugi::xml_node node) { + for (pugi::xml_node dag_node : node.children("dagmc_universe")) { + model::universes.push_back(std::make_unique(dag_node)); + model::universe_map[model::universes.back()->id_] = model::universes.size() - 1; + } +} + +void check_dagmc_root_univ() { + const auto& ru = model::universes[model::root_universe]; + if (ru->geom_type() == GeometryType::DAG) { + // if the root universe contains DAGMC geometry, warn the user + // if it does not contain a graveyard volume + auto dag_univ = dynamic_cast(ru.get()); + if (dag_univ && !dag_univ->has_graveyard()) { + warning("No graveyard volume found in the DagMC model. " + "This may result in lost particles and rapid simulation failure."); + } + } +} + +int32_t next_cell(DAGUniverse* dag_univ, DAGCell* cur_cell, DAGSurface* surf_xed) +{ + moab::EntityHandle surf = + surf_xed->dagmc_ptr()->entity_by_index(2, surf_xed->dag_index()); + moab::EntityHandle vol = + cur_cell->dagmc_ptr()->entity_by_index(3, cur_cell->dag_index()); + + moab::EntityHandle new_vol; + cur_cell->dagmc_ptr()->next_vol(surf, vol, new_vol); + + return cur_cell->dagmc_ptr()->index_by_handle(new_vol) + dag_univ->cell_idx_offset_; } -} -#endif +} // namespace openmc + +#else + +namespace openmc { + +void read_dagmc_universes(pugi::xml_node node) {}; +void check_dagmc_root_univ() {}; + +} // namespace openmc + +#endif // DAGMC diff --git a/src/finalize.cpp b/src/finalize.cpp index 22dc671836..6d207bd7a2 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -51,9 +51,6 @@ void free_memory() if (settings::event_based) { free_event_queues(); } -#ifdef DAGMC - free_memory_dagmc(); -#endif } } @@ -74,7 +71,6 @@ int openmc_finalize() settings::confidence_intervals = false; settings::create_fission_neutrons = true; settings::electron_treatment = ElectronTreatment::LED; - settings::dagmc = false; settings::delayed_photon_scaling = true; settings::energy_cutoff = {0.0, 1000.0, 0.0, 0.0}; settings::entropy_on = false; diff --git a/src/geometry.cpp b/src/geometry.cpp index 59607f637a..c93e1940b6 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -112,35 +112,13 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) // that. if (i_cell == C_NONE) { int i_universe = p.coord(p.n_coord() - 1).universe; - const auto& univ {*model::universes[i_universe]}; - const auto& cells { - !univ.partitioner_ - ? model::universes[i_universe]->cells_ - : univ.partitioner_->get_cells(p.r_local(), p.u_local()) - }; - - for (auto it = cells.cbegin(); it != cells.cend(); it++) { - i_cell = *it; - - // Make sure the search cell is in the same universe. - int i_universe = p.coord(p.n_coord() - 1).universe; - if (model::cells[i_cell]->universe_ != i_universe) continue; - - // Check if this cell contains the particle. - Position r {p.r_local()}; - Direction u {p.u_local()}; - auto surf = p.surface(); - if (model::cells[i_cell]->contains(r, u, surf)) { - p.coord(p.n_coord() - 1).cell = i_cell; - found = true; - break; - } - } - } - if (!found) { - return found; + const auto& univ {model::universes[i_universe]}; + found = univ->find_cell(p); } + if (!found) { return found; } + i_cell = p.coord(p.n_coord() - 1).cell; + // Announce the cell that the particle is entering. if (found && (settings::verbosity >= 10 || p.trace())) { auto msg = fmt::format(" Entering cell {}", model::cells[i_cell]->id_); diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 17000129d3..af6ebac9f8 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -42,13 +42,6 @@ void update_universe_cell_count(int32_t a, int32_t b) { void read_geometry_xml() { -#ifdef DAGMC - if (settings::dagmc) { - read_geometry_dagmc(); - return; - } -#endif - // Display output message write_message("Reading geometry XML file...", 5); @@ -73,8 +66,25 @@ void read_geometry_xml() read_cells(root); read_lattices(root); + // Check to make sure a boundary condition was applied to at least one + // surface + bool boundary_exists = false; + for (const auto& surf : model::surfaces) { + if (surf->bc_) { + boundary_exists = true; + break; + } + } + + if (settings::run_mode != RunMode::PLOTTING && !boundary_exists) { + fatal_error("No boundary conditions were applied to any surfaces!"); + } + // Allocate universes, universe cell arrays, and assign base universe model::root_universe = find_root_universe(); + + // if the root universe is DAGMC geometry, make sure the model is well-formed + check_dagmc_root_univ(); } //============================================================================== diff --git a/src/material.cpp b/src/material.cpp index 59d2f954e4..4e6dfbdf1b 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -14,7 +14,6 @@ #include "openmc/capi.h" #include "openmc/cross_sections.h" #include "openmc/container_util.h" -#include "openmc/dagmc.h" #include "openmc/error.h" #include "openmc/file_utils.h" #include "openmc/hdf5_interface.h" @@ -1223,24 +1222,14 @@ void read_materials_xml() pugi::xml_document doc; - bool using_dagmc_mats = false; -#ifdef DAGMC - if (settings::dagmc) { - using_dagmc_mats = read_uwuw_materials(doc); + // Check if materials.xml exists + std::string filename = settings::path_input + "materials.xml"; + if (!file_exists(filename)) { + fatal_error("Material XML file '" + filename + "' does not exist!"); } -#endif - - if (!using_dagmc_mats) { - // Check if materials.xml exists - std::string filename = settings::path_input + "materials.xml"; - if (!file_exists(filename)) { - fatal_error("Material XML file '" + filename + "' does not exist!"); - } - - // Parse materials.xml file and get root element - doc.load_file(filename.c_str()); - } + // Parse materials.xml file and get root element + doc.load_file(filename.c_str()); // Loop over XML material elements and populate the array. pugi::xml_node root = doc.document_element(); diff --git a/src/particle.cpp b/src/particle.cpp index fe2579593e..ef5c3199ad 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -9,6 +9,7 @@ #include "openmc/capi.h" #include "openmc/cell.h" #include "openmc/constants.h" +#include "openmc/dagmc.h" #include "openmc/error.h" #include "openmc/geometry.h" #include "openmc/hdf5_interface.h" @@ -285,6 +286,10 @@ Particle::event_collide() // Score flux derivative accumulators for differential tallies. if (!model::active_tallies.empty()) score_collision_derivative(*this); + + #ifdef DAGMC + history().reset(); + #endif } void @@ -318,9 +323,8 @@ void Particle::event_death() { #ifdef DAGMC - if (settings::dagmc) - history().reset(); -#endif + history().reset(); + #endif // Finish particle track output. if (write_track()) { @@ -377,6 +381,11 @@ Particle::cross_surface() int64_t idx = simulation::surf_source_bank.thread_safe_append(site); } + // if we're crossing a CSG surface, make sure the DAG history is reset + #ifdef DAGMC + if (surf->geom_type_ == GeometryType::CSG) history().reset(); + #endif + // Handle any applicable boundary conditions. if (surf->bc_ && settings::run_mode != RunMode::PLOTTING) { surf->bc_->handle_particle(*this, *surf); @@ -387,17 +396,18 @@ Particle::cross_surface() // SEARCH NEIGHBOR LISTS FOR NEXT CELL #ifdef DAGMC - if (settings::dagmc) { - auto cellp = dynamic_cast(model::cells[cell_last(0)].get()); - // TODO: off-by-one - auto surfp = - dynamic_cast(model::surfaces[std::abs(surface()) - 1].get()); - int32_t i_cell = next_cell(cellp, surfp) - 1; + // in DAGMC, we know what the next cell should be + if (surf->geom_type_ == GeometryType::DAG) { + auto surfp = dynamic_cast(surf); + auto cellp = dynamic_cast(model::cells[cell_last(n_coord() - 1)].get()); + auto univp = static_cast(model::universes[coord(n_coord() - 1).universe].get()); + // determine the next cell for this crossing + int32_t i_cell = next_cell(univp, cellp, surfp) - 1; // save material and temp material_last() = material(); sqrtkT_last() = sqrtkT(); // set new cell value - coord(0).cell = i_cell; + coord(n_coord() - 1).cell = i_cell; cell_instance() = 0; material() = model::cells[i_cell]->material_[0]; sqrtkT() = model::cells[i_cell]->sqrtkT_[0]; @@ -503,13 +513,11 @@ Particle::cross_reflective_bc(const Surface& surf, Direction new_u) // boundary, it is necessary to redetermine the particle's coordinates in // the lower universes. // (unless we're using a dagmc model, which has exactly one universe) - if (!settings::dagmc) { - n_coord() = 1; - if (!neighbor_list_find_cell(*this)) { - mark_as_lost("Couldn't find particle after reflecting from surface " + - std::to_string(surf.id_) + "."); - return; - } + n_coord() = 1; + if (surf.geom_type_ != GeometryType::DAG && !neighbor_list_find_cell(*this)) { + this->mark_as_lost("Couldn't find particle after reflecting from surface " + + std::to_string(surf.id_) + "."); + return; } // Set previous coordinate going slightly past surface crossing diff --git a/src/summary.cpp b/src/summary.cpp index 3d2c6816b4..e43982a6a3 100644 --- a/src/summary.cpp +++ b/src/summary.cpp @@ -89,14 +89,6 @@ void write_geometry(hid_t file) { auto geom_group = create_group(file, "geometry"); -#ifdef DAGMC - if (settings::dagmc) { - write_attribute(geom_group, "dagmc", 1); - close_group(geom_group); - return; - } -#endif - write_attribute(geom_group, "n_cells", model::cells.size()); write_attribute(geom_group, "n_surfaces", model::surfaces.size()); write_attribute(geom_group, "n_universes", model::universes.size()); diff --git a/src/surface.cpp b/src/surface.cpp index 76542fcd86..1de606198a 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -9,7 +9,6 @@ #include "openmc/array.h" #include "openmc/container_util.h" -#include "openmc/dagmc.h" #include "openmc/error.h" #include "openmc/hdf5_interface.h" #include "openmc/math_functions.h" @@ -198,21 +197,21 @@ Surface::diffuse_reflect(Position r, Direction u, uint64_t* seed) const return u/u.norm(); } -CSGSurface::CSGSurface() : Surface{} {}; -CSGSurface::CSGSurface(pugi::xml_node surf_node) : Surface{surf_node} {}; - void -CSGSurface::to_hdf5(hid_t group_id) const +Surface::to_hdf5(hid_t group_id) const { - std::string group_name {"surface "}; - group_name += std::to_string(id_); + hid_t surf_group = create_group(group_id, fmt::format("surface {}", id_)); - hid_t surf_group = create_group(group_id, group_name); + if (geom_type_ == GeometryType::DAG) { + write_string(surf_group, "geom_type", "dagmc", false); + } else if (geom_type_ == GeometryType::CSG) { + write_string(surf_group, "geom_type", "csg", false); - if (bc_) { - write_string(surf_group, "boundary_type", bc_->type(), false); - } else { - write_string(surf_group, "boundary_type", "transmission", false); + if (bc_) { + write_string(surf_group, "boundary_type", bc_->type(), false); + } else { + write_string(surf_group, "boundary_type", "transmission", false); + } } if (!name_.empty()) { @@ -224,60 +223,12 @@ CSGSurface::to_hdf5(hid_t group_id) const close_group(surf_group); } -//============================================================================== -// DAGSurface implementation -//============================================================================== -#ifdef DAGMC -DAGSurface::DAGSurface() : Surface{} {} // empty constructor - -double DAGSurface::evaluate(Position r) const -{ - return 0.0; -} - -double -DAGSurface::distance(Position r, Direction u, bool coincident) const -{ - moab::ErrorCode rval; - moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); - moab::EntityHandle hit_surf; - double dist; - double pnt[3] = {r.x, r.y, r.z}; - double dir[3] = {u.x, u.y, u.z}; - rval = dagmc_ptr_->ray_fire(surf, pnt, dir, hit_surf, dist, NULL, 0, 0); - MB_CHK_ERR_CONT(rval); - if (dist < 0.0) dist = INFTY; - return dist; -} - -Direction DAGSurface::normal(Position r) const -{ - moab::ErrorCode rval; - moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); - double pnt[3] = {r.x, r.y, r.z}; - double dir[3]; - rval = dagmc_ptr_->get_angle(surf, pnt, dir); - MB_CHK_ERR_CONT(rval); - return dir; -} - -Direction DAGSurface::reflect(Position r, Direction u, Particle* p) const -{ - Expects(p); - p->history().reset_to_last_intersection(); - moab::ErrorCode rval; - moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); - double pnt[3] = {r.x, r.y, r.z}; - double dir[3]; - rval = dagmc_ptr_->get_angle(surf, pnt, dir, &p->history()); - MB_CHK_ERR_CONT(rval); - p->last_dir() = u.reflect(dir); - return p->last_dir(); -} - -void DAGSurface::to_hdf5(hid_t group_id) const {} - -#endif +CSGSurface::CSGSurface() : Surface{} { + geom_type_ = GeometryType::CSG; +}; +CSGSurface::CSGSurface(pugi::xml_node surf_node) : Surface{surf_node} { + geom_type_ = GeometryType::CSG; +}; //============================================================================== // Generic functions for x-, y-, and z-, planes. @@ -1036,9 +987,6 @@ void read_surfaces(pugi::xml_node node) // Count the number of surfaces int n_surfaces = 0; for (pugi::xml_node surf_node : node.children("surface")) {n_surfaces++;} - if (n_surfaces == 0) { - fatal_error("No surfaces found in geometry.xml!"); - } // Loop over XML surface elements and populate the array. Keep track of // periodic surfaces. @@ -1183,19 +1131,6 @@ void read_surfaces(pugi::xml_node node) surf2.bc_ = surf1.bc_; } } - - // Check to make sure a boundary condition was applied to at least one - // surface - bool boundary_exists = false; - for (const auto& surf : model::surfaces) { - if (surf->bc_) { - boundary_exists = true; - break; - } - } - if (settings::run_mode != RunMode::PLOTTING && !boundary_exists) { - fatal_error("No boundary conditions were applied to any surfaces!"); - } } void free_memory_surfaces() diff --git a/tests/regression_tests/dagmc/legacy/inputs_true.dat b/tests/regression_tests/dagmc/legacy/inputs_true.dat index 769a3384c3..2f56410466 100644 --- a/tests/regression_tests/dagmc/legacy/inputs_true.dat +++ b/tests/regression_tests/dagmc/legacy/inputs_true.dat @@ -1,4 +1,8 @@ + + + + @@ -22,7 +26,6 @@ -4 -4 -4 4 4 4 - true diff --git a/tests/regression_tests/dagmc/legacy/test.py b/tests/regression_tests/dagmc/legacy/test.py index 5af18e5c8c..e0fccb9344 100644 --- a/tests/regression_tests/dagmc/legacy/test.py +++ b/tests/regression_tests/dagmc/legacy/test.py @@ -26,6 +26,10 @@ def model(): model.settings.dagmc = True + # geometry + dag_univ = openmc.DAGMCUniverse("dagmc.h5m") + model.geometry = openmc.Geometry(dag_univ) + # tally tally = openmc.Tally() tally.scores = ['total'] diff --git a/tests/regression_tests/dagmc/refl/inputs_true.dat b/tests/regression_tests/dagmc/refl/inputs_true.dat index 87ceb944d8..938916ece1 100644 --- a/tests/regression_tests/dagmc/refl/inputs_true.dat +++ b/tests/regression_tests/dagmc/refl/inputs_true.dat @@ -1,4 +1,11 @@ + + + + + + + eigenvalue 100 @@ -9,12 +16,11 @@ -4 -4 -4 4 4 4 - true - 1 + 2 1 diff --git a/tests/regression_tests/dagmc/refl/test.py b/tests/regression_tests/dagmc/refl/test.py index c451b61250..3fe41345cf 100644 --- a/tests/regression_tests/dagmc/refl/test.py +++ b/tests/regression_tests/dagmc/refl/test.py @@ -27,13 +27,18 @@ class UWUWTest(PyAPITestHarness): model.settings.export_to_xml() + # geometry + dag_univ = openmc.DAGMCUniverse("dagmc.h5m", auto_geom_ids=True) + model.geometry = openmc.Geometry(dag_univ) + # tally tally = openmc.Tally() tally.scores = ['total'] - tally.filters = [openmc.CellFilter(1)] + tally.filters = [openmc.CellFilter(2)] model.tallies = [tally] model.tallies.export_to_xml() + model.export_to_xml() def test_refl(): harness = UWUWTest('statepoint.5.h5') diff --git a/tests/regression_tests/dagmc/universes/__init__.py b/tests/regression_tests/dagmc/universes/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/dagmc/universes/dagmc.h5m b/tests/regression_tests/dagmc/universes/dagmc.h5m new file mode 100644 index 0000000000..d5f884db27 Binary files /dev/null and b/tests/regression_tests/dagmc/universes/dagmc.h5m differ diff --git a/tests/regression_tests/dagmc/universes/inputs_true.dat b/tests/regression_tests/dagmc/universes/inputs_true.dat new file mode 100644 index 0000000000..4443f9a35d --- /dev/null +++ b/tests/regression_tests/dagmc/universes/inputs_true.dat @@ -0,0 +1,55 @@ + + + + + + 24.0 24.0 + 2 2 + -24.0 -24.0 + +9 9 +9 9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 2 + + false + + diff --git a/tests/regression_tests/dagmc/universes/results_true.dat b/tests/regression_tests/dagmc/universes/results_true.dat new file mode 100644 index 0000000000..0dc1789d45 --- /dev/null +++ b/tests/regression_tests/dagmc/universes/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.436168E-01 2.905559E-02 diff --git a/tests/regression_tests/dagmc/universes/test.py b/tests/regression_tests/dagmc/universes/test.py new file mode 100644 index 0000000000..01963986c5 --- /dev/null +++ b/tests/regression_tests/dagmc/universes/test.py @@ -0,0 +1,80 @@ +import numpy as np + +import openmc +import openmc.lib + +import pytest +from tests.testing_harness import PyAPITestHarness + +pytestmark = pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), + reason="DAGMC CAD geometry is not enabled.") + + +class DAGMCUniverseTest(PyAPITestHarness): + + def _build_inputs(self): + model = openmc.model.Model() + + ### MATERIALS ### + fuel = openmc.Material(name='no-void fuel') + fuel.set_density('g/cc', 10.29769) + fuel.add_nuclide('U234', 0.93120485) + fuel.add_nuclide('U235', 0.00055815) + fuel.add_nuclide('U238', 0.022408) + fuel.add_nuclide('O16', 0.045829) + + cladding = openmc.Material(name='clad') + cladding.set_density('g/cc', 6.55) + cladding.add_nuclide('Zr90', 0.021827) + cladding.add_nuclide('Zr91', 0.00476) + cladding.add_nuclide('Zr92', 0.0072758) + cladding.add_nuclide('Zr94', 0.0073734) + cladding.add_nuclide('Zr96', 0.0011879) + + water = openmc.Material(name='water') + water.set_density('g/cc', 0.740582) + water.add_nuclide('H1', 0.049457) + water.add_nuclide('O16', 0.024672) + water.add_nuclide('B10', 8.0042e-06) + water.add_nuclide('B11', 3.2218e-05) + water.add_s_alpha_beta('c_H_in_H2O') + + model.materials = openmc.Materials([fuel, cladding, water]) + + ### GEOMETRY ### + # create the DAGMC universe + pincell_univ = openmc.DAGMCUniverse(filename='dagmc.h5m', auto_geom_ids=True) + + # create a 2 x 2 lattice using the DAGMC pincell + pitch = np.asarray((24.0, 24.0)) + lattice = openmc.RectLattice() + lattice.pitch = pitch + lattice.universes = [[pincell_univ] * 2] * 2 + lattice.lower_left = -pitch + + left = openmc.XPlane(x0=-pitch[0], name='left', boundary_type='reflective') + right = openmc.XPlane(x0=pitch[0], name='right', boundary_type='reflective') + front = openmc.YPlane(y0=-pitch[1], name='front', boundary_type='reflective') + back = openmc.YPlane(y0=pitch[1], name='back', boundary_type='reflective') + # clip the DAGMC geometry at +/- 10 cm w/ CSG planes + bottom = openmc.ZPlane(z0=-10.0, name='bottom', boundary_type='reflective') + top = openmc.ZPlane(z0=10.0, name='top', boundary_type='reflective') + + bounding_region = +left & -right & +front & -back & +bottom & -top + bounding_cell = openmc.Cell(fill=lattice, region=bounding_region) + + model.geometry = openmc.Geometry([bounding_cell]) + + # settings + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.inactive = 2 + model.settings.output = {'summary' : False} + + model.export_to_xml() + + +def test_univ(): + harness = DAGMCUniverseTest('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/dagmc/uwuw/inputs_true.dat b/tests/regression_tests/dagmc/uwuw/inputs_true.dat index 87ceb944d8..7d533c5db2 100644 --- a/tests/regression_tests/dagmc/uwuw/inputs_true.dat +++ b/tests/regression_tests/dagmc/uwuw/inputs_true.dat @@ -1,4 +1,11 @@ + + + + + + + eigenvalue 100 @@ -9,7 +16,6 @@ -4 -4 -4 4 4 4 - true diff --git a/tests/regression_tests/dagmc/uwuw/test.py b/tests/regression_tests/dagmc/uwuw/test.py index b4391d8e77..5c9777d481 100644 --- a/tests/regression_tests/dagmc/uwuw/test.py +++ b/tests/regression_tests/dagmc/uwuw/test.py @@ -27,13 +27,17 @@ class UWUWTest(PyAPITestHarness): model.settings.export_to_xml() + # geometry + dag_univ = openmc.DAGMCUniverse("dagmc.h5m") + model.geometry = openmc.Geometry(root=dag_univ) + # tally tally = openmc.Tally() tally.scores = ['total'] tally.filters = [openmc.CellFilter(1)] model.tallies = [tally] - model.tallies.export_to_xml() + model.export_to_xml() def test_uwuw(): harness = UWUWTest('statepoint.5.h5') diff --git a/tests/unit_tests/dagmc/test.py b/tests/unit_tests/dagmc/test.py index e5d7725551..b3e2c390f4 100644 --- a/tests/unit_tests/dagmc/test.py +++ b/tests/unit_tests/dagmc/test.py @@ -29,7 +29,9 @@ def dagmc_model(request): source = openmc.Source(space=source_box) model.settings.source = source - model.settings.dagmc = True + # geometry + dagmc_universe = openmc.DAGMCUniverse('dagmc.h5m') + model.geometry = openmc.Geometry(dagmc_universe) # tally tally = openmc.Tally() diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 7a3357d439..fdaf5c2db8 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -54,7 +54,6 @@ def test_export_to_xml(run_in_tmpdir): s.log_grid_bins = 2000 s.photon_transport = False s.electron_treatment = 'led' - s.dagmc = False # Make sure exporting XML works s.export_to_xml() @@ -111,4 +110,3 @@ def test_export_to_xml(run_in_tmpdir): assert s.log_grid_bins == 2000 assert not s.photon_transport assert s.electron_treatment == 'led' - assert not s.dagmc