From 65624df58da7a2cf63cc4dcc6299ef0add974e15 Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 3 Aug 2022 12:30:02 -0500 Subject: [PATCH] add chain_file, dilute_initial parameters to MicroXS.from_model --- docs/source/usersguide/depletion.rst | 6 +- openmc/deplete/microxs.py | 84 +++++++++++++++--- tests/micro_xs_simple.csv | 18 ++-- .../test_reference_fission_q.h5 | Bin 35688 -> 36312 bytes .../test_reference_source_rate.h5 | Bin 35688 -> 36312 bytes tests/regression_tests/microxs/test.py | 8 +- .../microxs/test_reference.csv | 20 +++-- 7 files changed, 107 insertions(+), 29 deletions(-) diff --git a/docs/source/usersguide/depletion.rst b/docs/source/usersguide/depletion.rst index 29bbcd1ed..821ff338b 100644 --- a/docs/source/usersguide/depletion.rst +++ b/docs/source/usersguide/depletion.rst @@ -216,7 +216,7 @@ and a path to a depletion chain file:: materials = openmc.Materials() ... - micro_xs = openmc.deplete.MicroXS() + micro_xs = openmc.deplete.MicroXS ... op = openmc.deplete.IndependentOperator(materials, micro_xs, chain_file) @@ -267,7 +267,9 @@ Users can generate the one-group microscopic cross sections needed by model = openmc.Model.from_xml() - micro_xs = openmc.deplete.MicroXS.from_model(model, model.materials[0]) + micro_xs = openmc.deplete.MicroXS.from_model(model, + model.materials[0], + chain_file) The :meth:`~openmc.deplete.MicroXS.from_model()` method will produce a :class:`~openmc.deplete.MicroXS` object with microscopic cross section data in diff --git a/openmc/deplete/microxs.py b/openmc/deplete/microxs.py index 89ad5e03c..09de090bf 100644 --- a/openmc/deplete/microxs.py +++ b/openmc/deplete/microxs.py @@ -8,13 +8,16 @@ import tempfile from pathlib import Path from pandas import DataFrame, read_csv, concat -from numpy import ndarray +import numpy as np from openmc.checkvalue import check_type, check_value, check_iterable_type from openmc.mgxs import EnergyGroups, ArbitraryXS, FissionXS -from openmc import Tallies, StatePoint +from openmc.data import DataLibrary +from openmc import Tallies, StatePoint, Materials -from .chain import REACTIONS + +from .chain import Chain, REACTIONS +from .coupled_operator import _find_cross_sections _valid_rxns = list(REACTIONS) _valid_rxns.append('fission') @@ -29,13 +32,8 @@ class MicroXS(DataFrame): def from_model(cls, model, reaction_domain, - reactions=['(n,gamma)', - '(n,2n)', - '(n,p)', - '(n,a)', - '(n,3n)', - '(n,4n)', - 'fission'], + chain_file, + dilute_initial=1.0e3, energy_bounds=(0, 20e6)): """Generate a one-group cross-section dataframe using OpenMC. Note that the ``openmc`` executable must be compiled. @@ -46,6 +44,14 @@ class MicroXS(DataFrame): OpenMC model object. Must contain geometry, materials, and settings. reaction_domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain in which to tally reaction rates. + chain_file : str + Path to the depletion chain XML file that will be used in depletion + simulation. Used to determine cross sections for materials not + present in the inital composition. + dilute_initial : float + Initial atom density [atoms/cm^3] to add for nuclides that + are zero in initial condition to ensure they exist in the cross + section data. Only done for nuclides with reaction rates. reactions : list of str, optional Reaction names to tally energy_bound : 2-tuple of float, optional @@ -63,6 +69,12 @@ class MicroXS(DataFrame): original_tallies = model.tallies tallies = Tallies() xs = {} + reactions, diluted_materials = cls._add_dilute_nuclides(chain_file, + model, + dilute_initial) + diluted_materials.export_to_xml('diluted_materials.xml') + model.materials = diluted_materials + for rxn in reactions: if rxn == 'fission': xs[rxn] = FissionXS(domain=reaction_domain, groups=groups, by_nuclide=True) @@ -94,6 +106,56 @@ class MicroXS(DataFrame): return micro_xs + @classmethod + def _add_dilute_nuclides(cls, chain_file, model, dilute_initial): + chain = Chain.from_xml(chain_file) + reactions = chain.reactions + cross_sections = _find_cross_sections(model) + nuclides_with_data = cls._get_nuclides_with_data(cross_sections) + burnable_nucs = [nuc.name for nuc in chain.nuclides + if nuc.name in nuclides_with_data] + diluted_materials = Materials() + for material in model.materials: + if material.depletable: + nuc_densities = material.get_nuclide_atom_densities() + dilute_density = 1.E-24 * dilute_initial + material.set_density('sum') + for nuc, density in nuc_densities.items(): + material.remove_nuclide(nuc) + material.add_nuclide(nuc, density) + for burn_nuc in burnable_nucs: + if burn_nuc not in nuc_densities: + material.add_nuclide(burn_nuc, + dilute_density) + diluted_materials.append(material) + return reactions, diluted_materials + + @staticmethod + def _get_nuclides_with_data(cross_sections): + """Loads cross_sections.xml file to find nuclides with neutron data + + Parameters + ---------- + cross_sections : str + Path to cross_sections.xml file + + Returns + ------- + nuclides : set of str + Set of nuclide names that have cross secton data + + """ + nuclides = set() + data_lib = DataLibrary.from_xml(cross_sections) + for library in data_lib.libraries: + if library['type'] != 'neutron': + continue + for name in library['materials']: + if name not in nuclides: + nuclides.add(name) + + return nuclides + @classmethod def from_array(cls, nuclides, reactions, data): """ @@ -162,6 +224,6 @@ class MicroXS(DataFrame): def _validate_micro_xs_inputs(nuclides, reactions, data): check_iterable_type('nuclides', nuclides, str) check_iterable_type('reactions', reactions, str) - check_type('data', data, ndarray, expected_iter_type=float) + check_type('data', data, np.ndarray, expected_iter_type=float) for reaction in reactions: check_value('reactions', reaction, _valid_rxns) diff --git a/tests/micro_xs_simple.csv b/tests/micro_xs_simple.csv index bc43600ba..787ce74c3 100644 --- a/tests/micro_xs_simple.csv +++ b/tests/micro_xs_simple.csv @@ -1,7 +1,13 @@ nuclide,"(n,gamma)",fission -U234,23.518634203050645,0.49531930067650976 -U235,10.621118186344665,49.10955932965905 -U238,0.8652742788116043,0.10579281644765708 -U236,9.095623870006154,0.32315392339237936 -O16,0.0029535689693132015,0.0 -O17,0.05980775766572907,0.0 +U234,22.231989822002465,0.49620744663749855 +U235,10.479008971197121,48.41787337164604 +U238,0.8673334105437324,0.10467880588762352 +U236,8.65171044607122,0.31948392400019293 +O16,7.497851000107524e-05,0.0 +O17,0.0004079227797153371,0.0 +I135,6.842395323713927,0.0 +Xe135,227463.86426990604,0.0 +Xe136,0.02317896034753588,0.0 +Cs135,2.1721665580713623,0.0 +Gd157,12786.09939237018,0.0 +Gd156,3.4006085445846983,0.0 diff --git a/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 b/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 index 6c1b3de849e1a8792b9ce9398c7e2e41ae410b5e..82bf11567363a56b84f87a204df7ea79d6f7e124 100644 GIT binary patch delta 1568 zcmY+EVN6?96vz860`E2(Ey0yX4du<%wGIXaYoXLa%OkFCuoaDFCMp%TRf&t$;DR_E zq953xjP@khxEK*-VGs@E4XukIG=!y@2`L7(!Kg5^SjkMfFQfayY~Q_Y*Cx&F`Jdl8 z_kZ5Ix3Mu4y@W>fDC+ec7(uK6KU&|MiwrOjFH+(`8ToLqY3~&Iyg(jG*nJ@lcG5&R znW*p=9!Cj_FEl2`3zhkJqlP3MbD?dXER}(y1 zp2kz-qW*H|GrMud5M0~$G}v~@L9T6r2yej7y*Ye>PxY4vHAIc_Qo$haUcU+$i;$ew$hEq|!hZ8B0y3xvXgd&=Vu;rv}qp)-ZKK z`z6SD7Fin1S|x;of}Se!Bh3Y99IW<*J_G}gaYm3H$)b*xPQap$14A;ctS9xHSj%oR z;H={iZxp-h#t;gc;70KYIQ68uuGdJYfQiy%k%j48>D}WrRwnP%UEuRrdA1U+nag+uxon9x4EM`moGss;uiZ=T~3ozc| zK;a?SYB8b6F!XioPx%m?!t8El)EBD9%3*>huUJPt;=CDTsw1Mn<20*;J1sgi6GKN>RisHV*7(eIZxP2X5w< zr8m~hC+^?wx+HX3R~9aJ9cx-L`=|en{kdjrQm}i6{(1kr`TBDwReXLNE?zizuBT_d z`Q6fX)e{X>0$ct2*TI-km_Xat-M8WUz(jOM%D%4`P~V-G zztOD`%&*O~xBWHMX=>ZJ6i5Zx~VS5pn^ydEcM22uIF2!uMFvO2rc*L_xNh zEQ%Yts2y&R(r=zT4~L?yveC1E5PMkZ{$wR(~O*Vcg6s)J+RugZOaN(QCgfHU3OD0d0Y9F>^}40uhz z3ta90ulEpbfCou1VUb!kNTX9yR)lacgt#~?1&^RRaj>5_^yM?DCmENN_F^$kU>7`i z!Hd|0;%#Eo^7AKttip5Dr-hG#R>UXdlc)FY!^y`~6AC5d_-LgDr>M#*tO-XFv;?@I zHprV7a~Q8a1{jc-kndk^Vx7mN3jW;}@A_6ImUd=;==b;aJ$j%CuKDD%j`82_c{(G! zWAfX&X`OU0>{N zX85-El>g?nOVukAYrdz)+jf{=Je{x2t$jT*GVS}-vUBf>bIY%^1)t35;MNsxPri=- E0CJB zS-`}>=(6t7r4c@q7?x!i!?+V=nCN^nK z|IhP#p7Ve1J*S09ls}5bohYA3ZW=?Z0H1fiHk&)Zz)GD7kLbvUgN4vek=Lj6s2EC0 zHXNY|SxGca)J9R!my{;Om0HuC_^OK}!?V&&X2QrKw+jqLo8hX57PZ6W1{;n(L}VNm zY8^0aWSNXu&jt|4^{Ia6YLaAVNrdXSnFKf*Pd8R`GS!7OB+gTgTU*Tqsg7Bb(iFrS zV!+yiAU9ZGnK$DC-Gvxp#vx?hgUc3R*8HT37tI8ZSxEDYD76Ika|w^ zus0cqv~h@U7yH{L5z2VsoBEev_hw7mU^}5wUP_ZiC)2sK-B+kKDR=4*;PtGbQVHk0 z@8S_3sf?=^2DyFed#IGba_65Y(hG}SCt9*3@+d6#cv0>M?CslF@*zfqQ+F}rNvQ*yju2eAVtpNmOO%o6zMK)KY1RbSdmLyw zA+ z{yQjM*zS)$JsSDsd%geX9WR``w0WEVm!Z=i9pCbXe_-b8(}m~i{a3ykXrGf{>~QP; z^z&x}hi)wY_!C5h8*jfgd-T{YVe0&~^TwL9!UwBYUb%EIEA0GX=tFY!@8~eUPbdeJONFoT}zq2 zKKr6u)2^wp?f1Sjx1n6{aWrt{LEmp>J#&FuTtuF-*5-TmKlAVy}SMqDrHdGg(gz6`zneJ!I{A`w+rvFP;N9CXBaXj_RH!C#bL-AP$VR) z?Zzl|@Bz)5Ylxx9K3UBu|7Iv^4(x;YWI1)M3456nPs*xthVHd z$(XcXWi0d&Apu_rT`=9mu?fY@4I;>!vy;W`#90Zf46d2XE(@7!n^}on2Fx~D#UWbz0eGsvB9mvwKWjp+1c^1kY94vN( zASW2-8norvt&p?wc%hSQzN{Z0igxO;rVgTdJK>j|SD`Rq1Y5HcJGwT(d8ZD~>?G=f zQ%_WMagcL`VA{t)!DoWJz~ii&Ahd;$TMuMhM0L1fb=L?;yUE$C?AZh-x?$CPLN0J1 z>{-5)pj}npTD?mWM86=Wr?ftyVlCA{69y zkwtYwFSVoHvic4<44kp9z&_uIc1B(-?Il6H;^VX%(aWPmk7Dc1AjIv}x{rg!Z|lj^ zTT$wzFwMyOH)jk{Tk4n9zd*8KTnmIr;Z3~WPk8z=ru*UTwo|wk1oHzAQ0@R++y5zR zfT2K__BW%T8(PvYETsvSjAO7d)@5ie4e(^xx>rDzFo z&R|uF7fKi}KL%Kllu|a{cXFQlzhy7vPQMt8UtIe0$`6KMI&-u8ZhAO4cI5hxqjz2l z#&?`g9FLlVEf42A$NqqiXFZqB-}!#<@ar3W z{Pyb=(eruR+U3bUG4t6{{Db&&Vzl+*ckLZ(qJOr$ZS-D$_;_@9<@dXn!~d6FqUpzJ X`lt2u#emio+?X}PHy`p_Ds}t|vOiXw diff --git a/tests/regression_tests/microxs/test.py b/tests/regression_tests/microxs/test.py index 89fdfcaeb..dbfe3357b 100644 --- a/tests/regression_tests/microxs/test.py +++ b/tests/regression_tests/microxs/test.py @@ -1,10 +1,14 @@ """Test one-group cross section generation""" +from pathlib import Path + import numpy as np import pytest import openmc from openmc.deplete import MicroXS +CHAIN_FILE = Path(__file__).parents[2] / "chain_simple.xml" + @pytest.fixture(scope="module") def model(): fuel = openmc.Material(name="uo2") @@ -24,8 +28,6 @@ def model(): radii = [0.42, 0.45] fuel.volume = np.pi * radii[0] ** 2 - clad.volume = np.pi * (radii[1]**2 - radii[0]**2) - water.volume = 1.24**2 - (np.pi * radii[1]**2) materials = openmc.Materials([fuel, clad, water]) @@ -45,6 +47,6 @@ def model(): def test_from_model(model): ref_xs = MicroXS.from_csv('test_reference.csv') - test_xs = MicroXS.from_model(model, model.materials[0]) + test_xs = MicroXS.from_model(model, model.materials[0], CHAIN_FILE) np.testing.assert_allclose(test_xs, ref_xs, rtol=1e-11) diff --git a/tests/regression_tests/microxs/test_reference.csv b/tests/regression_tests/microxs/test_reference.csv index 0c47409b0..787ce74c3 100644 --- a/tests/regression_tests/microxs/test_reference.csv +++ b/tests/regression_tests/microxs/test_reference.csv @@ -1,7 +1,13 @@ -nuclide,"(n,gamma)","(n,2n)","(n,p)","(n,a)","(n,3n)","(n,4n)",fission -U234,23.518634203050674,0.0008255330840536984,0.0,0.0,9.404554397521455e-07,0.0,0.49531930067650964 -U235,10.621118186344795,0.004359401013759254,0.0,0.0,7.2974901306692395e-06,0.0,49.10955932965902 -U238,0.8652742788116055,0.005661917442209096,0.0,0.0,4.92273921631416e-05,0.0,0.10579281644765708 -U236,9.095623870006163,0.0024373322002926834,0.0,0.0,1.966889146690413e-05,0.0,0.3231539233923791 -O16,7.511380881289377e-05,0.0,1.3764104470470622e-05,0.002862620940027927,0.0,0.0,0.0 -O17,0.00041221042693945085,1.9826700699084533e-05,7.764409141772239e-06,0.05938517754333328,0.0,0.0,0.0 +nuclide,"(n,gamma)",fission +U234,22.231989822002465,0.49620744663749855 +U235,10.479008971197121,48.41787337164604 +U238,0.8673334105437324,0.10467880588762352 +U236,8.65171044607122,0.31948392400019293 +O16,7.497851000107524e-05,0.0 +O17,0.0004079227797153371,0.0 +I135,6.842395323713927,0.0 +Xe135,227463.86426990604,0.0 +Xe136,0.02317896034753588,0.0 +Cs135,2.1721665580713623,0.0 +Gd157,12786.09939237018,0.0 +Gd156,3.4006085445846983,0.0