Chi-Tech
single_state_mgxs_01_readfromchi.cc
Go to the documentation of this file.
1#include "single_state_mgxs.h"
2
3#include "chi_runtime.h"
4#include "chi_log.h"
5
6#include <string>
7#include <algorithm>
8#include <numeric>
9
10// See doc_ChiFormatXS.h for cross-section format
11
12//###################################################################
13/**This method populates a transport cross section from
14 * a Chi cross section file.*/
16 MakeFromChiXSFile(const std::string &file_name)
17{
18 Clear();
19
20 //============================================================
21 // Open Chi XS file
22 //============================================================
23
25 << "Reading Chi cross section file \"" << file_name << "\"\n";
26
27 //opens and checks if open
28 std::ifstream file;
29 file.open(file_name);
30 if (not file.is_open())
31 throw std::runtime_error(
32 "Failed to open Chi cross section file "
33 "\"" + file_name + "\" in call to " +
34 std::string(__FUNCTION__));
35
36 //============================================================
37 // Define utility functions for parsing
38 //============================================================
39
40 //##################################################
41 /// Lambda for reading group structure data.
42 auto ReadGroupStructure =
43 [](const std::string& keyword,
44 std::vector<std::vector<double>>& destination,
45 const unsigned int G, //# of groups
46 std::ifstream& file,
47 std::istringstream& line_stream,
48 unsigned int& line_number)
49 {
50 //init storage
51 destination.assign(G, std::vector<double>(2, 0.0));
52
53 //bookkeeping
54 std::string line;
55 int group;
56 double high;
57 double low;
58 unsigned int count = 0;
59
60 //read the block
61 std::getline(file, line);
62 line_stream = std::istringstream(line);
63 ++line_number;
64 while (line != keyword + "_END")
65 {
66 //get data from current line
67 line_stream >> group >> high >> low;
68 destination.at(group).at(0) = high;
69 destination.at(group).at(1) = low;
70 if (count++ >= G)
71 throw std::runtime_error(
72 "Too many entries encountered when parsing "
73 "group structure.\nThe expected number of entries "
74 "is " + std::to_string(G) + ".");
75
76 //go to next line
77 std::getline(file, line);
78 line_stream = std::istringstream(line);
79 ++line_number;
80 }
81 };
82
83 //##################################################
84 /// Lambda for reading vector data.
85 auto Read1DData =
86 [](const std::string& keyword,
87 std::vector<double>& destination,
88 const unsigned int N,
89 std::ifstream& file,
90 std::istringstream& line_stream,
91 unsigned int& line_number)
92 {
93 //init storage
94 destination.assign(N, 0.0);
95
96 //bookkeeping
97 std::string line;
98 int i;
99 double value;
100 unsigned int count = 0;
101
102 //read the bloc
103 std::getline(file, line);
104 line_stream = std::istringstream(line);
105 ++line_number;
106 while (line != keyword + "_END")
107 {
108 //get data from current line
109 line_stream >> i >> value;
110 destination.at(i) = value;
111 if (count++ >= N)
112 throw std::runtime_error(
113 "To many entries encountered when parsing "
114 "1D data.\nThe expected number of entries is " +
115 std::to_string(N));
116
117 //go to next line
118 std::getline(file, line);
119 line_stream = std::istringstream(line);
120 ++line_number;
121 }
122 };
123
124 //##################################################
125 /// Lambda for reading 2D data
126 auto Read2DData =
127 [](const std::string& keyword,
128 const std::string& entry_prefix,
129 std::vector<std::vector<double>>& destination,
130 const unsigned int n_rows,
131 const unsigned int n_cols,
132 std::ifstream& file,
133 std::istringstream& line_stream,
134 unsigned int& line_number)
135 {
136 //init storage
137 destination.clear();
138 for (unsigned int i = 0; i < n_rows; ++i)
139 destination.emplace_back(n_cols, 0.0);
140
141 //bookkeeping
142 std::string word, line;
143 double value;
144 unsigned int i;
145 unsigned int j;
146
147 //read the block
148 std::getline(file, line);
149 line_stream = std::istringstream(line);
150 ++line_number;
151 while (line != keyword + "_END")
152 {
153 //check that this line contains an entry
154 line_stream >> word;
155 if (word == entry_prefix)
156 {
157 //get data from current line
158 line_stream >> i >> j >> value;
159 if (entry_prefix == "G_PRECURSOR_VAL")
160 destination.at(j).at(i) = value; //hack
161 else
162 destination.at(i).at(j) = value;
163 }
164
165 //go to next line
166 std::getline(file, line);
167 line_stream = std::istringstream(line);
168 ++line_number;
169 }
170 };
171
172
173 //##################################################
174 /// Lambda for reading transfer matrix data.
175 auto ReadTransferMatrices =
176 [](const std::string& keyword,
177 std::vector<chi_math::SparseMatrix>& destination,
178 const unsigned int M, //# of moments
179 const unsigned int G, //# of groups
180 std::ifstream& file,
181 std::istringstream& line_stream,
182 unsigned int& line_number)
183 {
184 //init storage
185 destination.clear();
186 for (unsigned int i = 0; i < M; ++i)
187 destination.emplace_back(G, G);
188
189 //bookkeeping
190 std::string word, line;
191 double value;
192 unsigned int ell;
193 unsigned int group;
194 unsigned int gprime;
195
196 //read the block
197 std::getline(file, line);
198 line_stream = std::istringstream(line);
199 ++line_number;
200 while (line != keyword + "_END")
201 {
202 //check that this line contains an entry
203 line_stream >> word;
204 if (word == "M_GPRIME_G_VAL")
205 {
206 //get data from current line
207 line_stream >> ell >> gprime >> group >> value;
208 destination.at(ell).Insert(group, gprime, value);
209 }
210
211 //go to next line
212 std::getline(file, line);
213 line_stream = std::istringstream(line);
214 ++line_number;
215 }
216 };
217
218 //##################################################
219 /// Lambda for checking for all non-negative values.
220 auto IsNonNegative =
221 [](const std::vector<double>& vec)
222 {
223 return not std::any_of(vec.begin(), vec.end(),
224 [](double x) { return x < 0.0; });
225 };
226
227 //##################################################
228 /// Lambda for checking for all strictly positive values.
229 auto IsPositive =
230 [](const std::vector<double>& vec)
231 {
232 return not std::any_of(vec.begin(), vec.end(),
233 [](double x) { return x <= 0.0; });
234 };
235
236 //##################################################
237 /// Lambda for checking for any non-zero values.
238 auto HasNonZero =
239 [](const std::vector<double> & vec)
240 {
241 return std::any_of(vec.begin(), vec.end(),
242 [](double x) { return x > 0.0; });
243 };
244
245 //============================================================
246 // Read the Chi XS file
247 //============================================================
248
249 //TODO: Determine whether or not to allow specification of a
250 // data block without any data. Currently, if a data block
251 // is specified and no values are present, the std::any_of
252 // checks will evaluate false if expected data is not present.
253
254 std::vector<double> decay_constants;
255 std::vector<double> fractional_yields;
256 std::vector<std::vector<double>> emission_spectra;
257 std::vector<double> nu, nu_prompt, nu_delayed, beta;
258 std::vector<double> chi, chi_prompt;
259
260 std::string word, line;
261 unsigned int line_number = 0;
262 while (std::getline(file, line))
263 {
264 std::istringstream line_stream(line);
265 line_stream >> word;
266
267 //parse number of groups
268 if (word == "NUM_GROUPS")
269 {
270 int G;
271 line_stream >> G;
272 if (G <= 0)
273 throw std::logic_error(
274 "The specified number of energy groups "
275 "must be strictly positive.");
276 num_groups_ = G;
277 }
278
279 //parse the number of scattering moments
280 if (word == "NUM_MOMENTS")
281 {
282 int M;
283 line_stream >> M;
284 if (M < 0)
285 throw std::logic_error(
286 "The specified number of scattering moments "
287 "must be non-negative.");
288 scattering_order_ = std::max(0, M - 1);
289 }
290
291 //parse the number of precursors species
292 if (word == "NUM_PRECURSORS")
293 {
294 int J;
295 line_stream >> J;
296 if (J < 0)
297 throw std::logic_error(
298 "The specified number of delayed neutron "
299 "precursors must be non-negative.");
300 num_precursors_ = J;
302 }
303
304 //parse nuclear data
305 try
306 {
307 auto& ln = line_number;
308 auto& ls = line_stream;
309 auto& f = file;
310 auto& fw = word;
311
312 //============================================================
313 // Group Structure Data
314 //============================================================
315
316 if (fw == "GROUP_STRUCTURE_BEGIN")
317 ReadGroupStructure("GROUP_STRUCTURE",
318 e_bounds_, num_groups_, f, ls, ln);
319
320 if (fw == "INV_VELOCITY_BEGIN")
321 {
322 Read1DData("INV_VELOCITY", inv_velocity_, num_groups_, f, ls, ln);
323
324 if (not IsPositive(inv_velocity_))
325 throw std::logic_error(
326 "Invalid inverse velocity value encountered.\n"
327 "Only strictly positive values are permitted.");
328 }
329 if (fw == "VELOCITY_BEGIN" and inv_velocity_.empty())
330 {
331 Read1DData("VELOCITY", inv_velocity_, num_groups_, f, ls, ln);
332
333 if (not IsPositive(inv_velocity_))
334 throw std::logic_error(
335 "Invalid velocity value encountered.\n"
336 "Only strictly positive values are permitted.");
337
338 //compute inverse
339 for (unsigned int g = 0; g < num_groups_; ++g)
340 inv_velocity_[g] = 1.0 / inv_velocity_[g];
341 }
342
343 //==================================================
344 // Cross Section Data
345 //==================================================
346
347 if (fw == "SIGMA_T_BEGIN")
348 {
349 Read1DData("SIGMA_T", sigma_t_, num_groups_, f, ls, ln);
350
351 if (not IsNonNegative(sigma_t_))
352 throw std::logic_error(
353 "Invalid total cross section value encountered.\n"
354 "Negative values are not permitted.");
355 }//if sigma_t
356
357 if (fw == "SIGMA_A_BEGIN")
358 {
359 Read1DData("SIGMA_A", sigma_a_, num_groups_, f, ls, ln);
360
361 if (not IsNonNegative(sigma_a_))
362 throw std::logic_error(
363 "Invalid absorption cross section value encountered.\n"
364 "Negative values are not permitted.");
365 }//if sigma_a
366
367 if (fw == "SIGMA_F_BEGIN")
368 {
369 Read1DData("SIGMA_F", sigma_f_, num_groups_, f, ls, ln);
370
371 if (not HasNonZero(sigma_f_))
372 {
374 << "The fission cross section specified in "
375 << "\"" << file_name << "\" is uniformly zero..."
376 << "Clearing it.";
377 sigma_f_.clear();
378 }
379 else if (not IsNonNegative(sigma_f_))
380 throw std::logic_error(
381 "Invalid fission cross section value encountered.\n"
382 "Negative values are not permitted.");
383 }//if sigma_f
384
385 if (fw == "NU_SIGMA_F_BEGIN")
386 {
387 Read1DData("NU_SIGMA_F", nu_sigma_f_, num_groups_, f, ls, ln);
388
389 if (not HasNonZero(nu_sigma_f_))
390 {
392 << "The production cross-section specified in "
393 << "\"" << file_name << "\" is uniformly zero..."
394 << "Clearing it.";
395 nu_sigma_f_.clear();
396 }
397 else if (not IsNonNegative(nu_sigma_f_))
398 throw std::logic_error(
399 "Invalid production cross section value encountered.\n"
400 "Negative values are not permitted.");
401 }//if nu_sigma_f
402
403 //==================================================
404 // Neutrons Per Fission
405 //==================================================
406
407 if (fw == "NU_BEGIN")
408 {
409 Read1DData("NU", nu, num_groups_, f, ls, ln);
410
411 if (not HasNonZero(nu))
412 {
414 << "The total fission neutron yield specified in "
415 << "\"" << file_name << "\" is uniformly zero..."
416 << "Clearing it.";
417 nu.clear();
418 }
419 else if (std::any_of(nu.begin(), nu.end(),
420 [](double x)
421 { return not (x == 0.0 or x > 1.0); }))
422 throw std::logic_error(
423 "Invalid total fission neutron yield value encountered.\n"
424 "Only values strictly greater than one, or zero, are "
425 "permitted.");
426
427 //compute prompt/delayed nu, if needed
428 if (num_precursors_ > 0 and
429 not nu.empty() and not beta.empty() and
430 nu_prompt.empty() and nu_delayed.empty())
431 {
432 nu_prompt.assign(num_groups_, 0.0);
433 nu_delayed.assign(num_groups_, 0.0);
434 for (unsigned int g = 0; g < num_groups_; ++g)
435 {
436 nu_prompt[g] = (1.0 - beta[g]) * nu[g];
437 nu_delayed[g] = beta[g] * nu[g];
438 }
439 }
440 }//if nu
441
442 if (fw == "NU_PROMPT_BEGIN")
443 {
444 Read1DData("NU_PROMPT", nu_prompt, num_groups_, f, ls, ln);
445
446 if (not HasNonZero(nu_prompt))
447 {
449 << "The prompt fission neutron yield specified in "
450 << "\"" << file_name << "\" is uniformly zero..."
451 << "Clearing it.";
452 nu_prompt.clear();
453 }
454 else if (std::any_of(nu_prompt.begin(), nu_prompt.end(),
455 [](double x)
456 { return not (x == 0.0 or x > 1.0); }))
457 throw std::logic_error(
458 "Invalid prompt fission neutron yield value encountered.\n"
459 "Only values strictly greater than one, or zero, are "
460 "permitted.");
461 }
462
463 if (fw == "NU_DELAYED_BEGIN")
464 {
465 Read1DData("NU_DELAYED", nu_delayed, num_groups_, f, ls, ln);
466
467 if (not HasNonZero(nu_delayed))
468 {
470 << "The delayed fission neutron yield specified in "
471 << "\"" << file_name << "\" is uniformly zero..."
472 << "Clearing it.";
473 nu_prompt.clear();
474 }
475 else if (not IsNonNegative(nu_delayed))
476 throw std::logic_error(
477 "Invalid delayed fission neutron yield value encountered.\n"
478 "Only non-negative values are permitted.");
479 }
480
481 if (fw == "BETA_BEGIN")
482 {
483 Read1DData("BETA", beta, num_groups_, f, ls, ln);
484
485 if (not HasNonZero(beta))
486 {
488 << "The delayed neutron fraction specified in "
489 << "\"" << file_name << "\" is uniformly zero..."
490 << "Clearing it.";
491 beta.clear();
492 }
493 else if (std::any_of(beta.begin(), beta.end(),
494 [](double x)
495 { return not (x >= 0.0 and x <= 1.0); }))
496 throw std::logic_error(
497 "Invalid delayed neutron fraction value encountered.\n"
498 "Only values in the range [0.0, 1.0] are permitted.");
499
500 //compute prompt/delayed nu, if needed
501 if (num_precursors_ > 0 and
502 not nu.empty() and not beta.empty() and
503 nu_prompt.empty() and nu_delayed.empty())
504 {
505 nu_prompt.assign(num_groups_, 0.0);
506 nu_delayed.assign(num_groups_, 0.0);
507 for (unsigned int g = 0; g < num_groups_; ++g)
508 {
509 nu_prompt[g] = (1.0 - beta[g]) * nu[g];
510 nu_delayed[g] = beta[g] * nu[g];
511 }
512 }
513 }//if beta
514
515 //==================================================
516 // Fission/Emission Spectra
517 //==================================================
518
519 if (fw == "CHI_BEGIN")
520 {
521 Read1DData("CHI", chi, num_groups_, f, ls, ln);
522
523 if (not HasNonZero(chi))
524 throw std::logic_error(
525 "Invalid steady-state fission spectrum encountered.\n"
526 "No non-zero values found.");
527 if (not IsNonNegative(chi))
528 throw std::logic_error(
529 "Invalid steady-state fission spectrum value encountered.\n"
530 "Only non-negative values are permitted.");
531
532 //normalizing
533 double sum = std::accumulate(chi.begin(), chi.end(), 0.0);
534 std::transform(chi.begin(), chi.end(), chi.begin(),
535 [sum](double& x) { return x / sum; });
536 }//if chi
537
538 if (fw == "CHI_PROMPT_BEGIN")
539 {
540 Read1DData("CHI_PROMPT", chi_prompt, num_groups_, f, ls, ln);
541
542 if (not HasNonZero(chi_prompt))
543 throw std::logic_error(
544 "Invalid prompt fission spectrum encountered.\n"
545 "No non-zero values found.");
546 if (not IsNonNegative(chi_prompt))
547 throw std::logic_error(
548 "Invalid prompt fission spectrum value encountered.\n"
549 "Only non-negative values are permitted.");
550
551 //normalizing
552 double sum = std::accumulate(chi_prompt.begin(), chi_prompt.end(), 0.0);
553 std::transform(chi_prompt.begin(),
554 chi_prompt.end(),
555 chi_prompt.begin(),
556 [sum](double& x) { return x / sum; });
557
558 }//if prompt chi
559
560 if (num_precursors_ > 0 and fw == "CHI_DELAYED_BEGIN")
561 {
562 //TODO: Should the be flipped to PRECURSOR_G_VAL?
563 Read2DData("CHI_DELAYED", "G_PRECURSOR_VAL",
564 emission_spectra, num_precursors_, num_groups_, f, ls, ln);
565
566 for (unsigned int j = 0; j < num_precursors_; ++j)
567 {
568 if (not HasNonZero(emission_spectra[j]))
569 throw std::logic_error(
570 "Invalid delayed emission spectrum encountered for "
571 "precursor species " + std::to_string(j) + ".\n" +
572 "No non-zero values found.");
573 if (not IsNonNegative(emission_spectra[j]))
574 throw std::logic_error(
575 "Invalid delayed emission spectrum value encountered "
576 "for precursor species " + std::to_string(j) + ".\n" +
577 "Only non-negative values are permitted.");
578
579 //normalizing
580 double sum = std::accumulate(emission_spectra[j].begin(),
581 emission_spectra[j].end(), 0.0);
582 std::transform(emission_spectra[j].begin(),
583 emission_spectra[j].end(),
584 emission_spectra[j].begin(),
585 [sum](double& x) { return x / sum; });
586 }
587 }//if delayed chi
588
589 //==================================================
590 // Delayed Neutron Precursor Data
591 //==================================================
592
593 if (num_precursors_ > 0)
594 {
595 if (fw == "PRECURSOR_DECAY_CONSTANTS_BEGIN")
596 {
597 Read1DData("PRECURSOR_DECAY_CONSTANTS",
598 decay_constants, num_precursors_, f, ls, ln);
599
600 if (not IsPositive(decay_constants))
601 throw std::logic_error(
602 "Invalid precursor decay constant value encountered.\n"
603 "Only strictly positive values are permitted.");
604 }//if decay constants
605
606 if (fw == "PRECURSOR_FRACTIONAL_YIELDS_BEGIN")
607 {
608 Read1DData("PRECURSOR_FRACTIONAL_YIELDS",
609 fractional_yields, num_precursors_, f, ls, ln);
610
611 if (not HasNonZero(fractional_yields))
612 throw std::logic_error(
613 "Invalid precursor fractional yields encountered.\n"
614 "No non-zero values found.");
615 if (std::any_of(fractional_yields.begin(),
616 fractional_yields.end(),
617 [](double x) { return not (x >= 0.0 and x <= 1.0); }))
618 throw std::logic_error(
619 "Invalid precursor fractional yield value encountered.\n"
620 "Only values in the range [0.0, 1.0] are permitted.");
621
622 //normalizing
623 double sum = std::accumulate(fractional_yields.begin(),
624 fractional_yields.end(), 0.0);
625 std::transform(fractional_yields.begin(),
626 fractional_yields.end(),
627 fractional_yields.begin(),
628 [sum](double& x) { return x / sum; });
629 }
630 }
631
632 //==================================================
633 // Transfer Data
634 //==================================================
635
636 if (fw == "TRANSFER_MOMENTS_BEGIN")
637 ReadTransferMatrices("TRANSFER_MOMENTS", transfer_matrices_,
638 scattering_order_ + 1, num_groups_, f, ls, ln);
639
640 if (fw == "PRODUCTION_MATRIX_BEGIN")
641 Read2DData("PRODUCTION_MATRIX", "GPRIME_G_VAL",
643 }//try
644
645 catch (const std::runtime_error& err)
646 {
647 throw std::runtime_error(
648 "Error reading Chi cross section file "
649 "\"" + file_name + "\".\n" +
650 "Line number " + std::to_string(line_number) +
651 "\n" + err.what());
652 }
653
654 catch (const std::logic_error& err)
655 {
656 throw std::logic_error(
657 "Error reading Chi cross section file "
658 "\"" + file_name + "\".\n" +
659 "Line number " + std::to_string(line_number) +
660 "\n" + err.what());
661 }
662
663 catch (...)
664 {
665 throw std::runtime_error(
666 "Unknown error encountered in " +
667 std::string (__FUNCTION__ ));
668 }
669
670 word = "";
671 }//while not EOF, read each lines
672 file.close();
673
674 if (sigma_a_.empty())
677
678 //============================================================
679 // Compute and check fission data
680 //============================================================
681
682 //determine if the material is fissionable
683 is_fissionable_ = not sigma_f_.empty() or not nu_sigma_f_.empty() or
684 not production_matrix_.empty();
685
686 //clear fission data if not fissionable
687 if (not is_fissionable_)
688 {
689 sigma_f_.clear();
690 nu_sigma_f_.clear();
691 nu_prompt_sigma_f_.clear();
692 nu_delayed_sigma_f_.clear();
693 precursors_.clear();
694 }//if not fissionable
695
696 //otherwise, check and set the fission data
697 else
698 {
699 //check vector data inputs
700 if (production_matrix_.empty())
701 {
702 //check for non-delayed fission neutron yield data
703 if (nu.empty() and nu_prompt.empty())
704 throw std::logic_error(
705 "Invalid fission neutron yield specification encountered.\n"
706 "Either the total or prompt fission neutron yield must be "
707 "specified.");
708
709 if (not nu.empty() and not nu_prompt.empty())
710 throw std::logic_error(
711 "Ambiguous fission neutron yield data specified.\n"
712 "Either the total or prompt fission neutron yield "
713 "must be specified, not both.");
714
715 //check for fission spectrum data
716 if (chi.empty() and chi_prompt.empty())
717 throw std::logic_error(
718 "Invalid fission spectrum specification encountered.\n"
719 "Either the steady-state or prompt fission spectrum must be "
720 "specified.");
721
722 if (not chi.empty() and not chi_prompt.empty())
723 throw std::logic_error(
724 "Ambiguous fission spectrum data specified.\n"
725 "Either the steady-state or prompt fission spectrum "
726 "must be specified, not both.");
727
728 //check for compatibility
729 if ((not nu.empty() and chi.empty()) or
730 (nu.empty() and not chi.empty()) or
731 (not nu_prompt.empty() and chi_prompt.empty()) or
732 (nu_prompt.empty() and not chi_prompt.empty()))
733 throw std::logic_error(
734 "Ambiguous fission data specified.\n"
735 "Either the total fission neutron yield and steady-state "
736 "fission spectrum or the prompt fission neutron yield and "
737 "prompt fission spectrum must be specified.");
738
739 //initialize total fission neutron yield
740 //do this only when prompt is specified
741 if (not nu_prompt.empty())
742 {
743 nu.assign(num_groups_, 0.0);
744 for (unsigned int g = 0; g < num_groups_; ++g)
745 nu[g] = nu_prompt[g];
746 }
747
748 //check delayed neutron data
749 if (num_precursors_ > 0)
750 {
751 //check that prompt data was specified
752 if (chi_prompt.empty() or nu_prompt.empty())
753 throw std::logic_error(
754 "Invalid fission data specification encountered.\n"
755 "When delayed neutron precursors are present, "
756 "prompt fission data must be specified.");
757
758 //check that delayed fission neutron yield was specified
759 if (nu_delayed.empty() or
760 decay_constants.empty() or
761 fractional_yields.empty() or
762 std::any_of(emission_spectra.begin(),
763 emission_spectra.end(),
764 [](const std::vector<double>& x)
765 { return x.empty(); }))
766 throw std::logic_error(
767 "Invalid fission data specification encountered.\n"
768 "When delayed neutron precursors are present, "
769 "the delayed fission neutron yield, delayed neutron "
770 "precursor decay constants, fractional yields, and "
771 "emission spectra must all be specified.");
772
773 //add delayed fission neutron yield to total
774 for (unsigned int g = 0; g < num_groups_; ++g)
775 nu[g] += nu_delayed[g];
776
777 //add data to precursor structs
778 for (unsigned int j = 0; j < num_precursors_; ++j)
779 {
780 precursors_[j].decay_constant = decay_constants[j];
781 precursors_[j].fractional_yield = fractional_yields[j];
782 precursors_[j].emission_spectrum = emission_spectra[j];
783 }
784 }
785
786 //compute fission cross section
787 if (sigma_f_.empty() and not nu_sigma_f_.empty())
788 {
789 sigma_f_.assign(num_groups_, 0.0);
790 for (unsigned int g = 0; g < num_groups_; ++g)
791 if (nu_sigma_f_[g] > 0.0)
792 sigma_f_[g] = nu_sigma_f_[g] / nu[g];
793 }
794
795 //compute total production cross section
796 nu_sigma_f_.assign(num_groups_, 0.0);
797 for (unsigned int g = 0; g < num_groups_; ++g)
798 nu_sigma_f_[g] = nu[g] * sigma_f_[g];
799
800 //compute prompt production cross section
801 if (not nu_prompt.empty())
802 {
803 nu_prompt_sigma_f_.assign(num_groups_, 0.0);
804 for (unsigned int g = 0; g < num_groups_; ++g)
805 nu_prompt_sigma_f_[g] = nu_prompt[g] * sigma_f_[g];
806 }
807
808 //compute delayed production cross section
809 if (not nu_delayed.empty())
810 {
811 nu_delayed_sigma_f_.assign(num_groups_, 0.0);
812 for (unsigned int g = 0; g < num_groups_; ++g)
813 nu_delayed_sigma_f_[g] = nu_delayed[g] * sigma_f_[g];
814 }
815
816 //compute production matrix
817 const auto chi_ = not chi_prompt.empty()? chi_prompt : chi;
818 const auto nu_sigma_f =
819 not nu_prompt.empty() ? nu_prompt_sigma_f_ : nu_sigma_f_;
820
821 for (unsigned int g = 0; g < num_groups_; ++g)
822 {
823 std::vector<double> prod;
824 for (unsigned int gp = 0.0; gp < num_groups_; ++gp)
825 prod.push_back(chi_[g] * nu_sigma_f[gp]);
826 production_matrix_.push_back(prod);
827 }
828 }//if production_matrix empty
829
830 else
831 {
832 //TODO: Develop an implementation for multi-particle delayed
833 // neutron data. The primary challenge in this is that
834 // different precursor species exist for neutron-induced
835 // fission than for photo-fission.
836
837 //throw error if precursors are present
838 if (num_precursors_ > 0)
839 throw std::runtime_error(
840 "This setup has not been implemented.\n"
841 "Currently, when a production matrix is specified, "
842 "no delayed neutron precursors are allowed.");
843
844 //check for fission cross sections
845 if (sigma_f_.empty())
846 throw std::logic_error(
847 "Invalid fission data specification encountered.\n"
848 "When a production matrix is specified, the fission "
849 "cross sections must also be specified.");
850
851 //compute production cross section
852 nu_sigma_f_.assign(num_groups_, 0.0);
853 for (unsigned int g = 0; g < num_groups_; ++g)
854 for (unsigned int gp = 0; gp < num_groups_; ++gp)
855 nu_sigma_f_[gp] += production_matrix_[g][gp];
856
857 //check for reasonable fission neutron yield
858 nu.assign(num_groups_, 0.0);
859 for (unsigned int g = 0; g < num_groups_; ++g)
860 if (sigma_f_[g] > 0.0)
861 nu[g] = nu_sigma_f_[g] / sigma_f_[g];
862
863 if (std::any_of(nu.begin(), nu.end(),
864 [](double x)
865 { return not (x == 0.0 or (x > 1.0 and x < 10.0));}))
866 throw std::logic_error(
867 "Incompatible fission data encountered.\n"
868 "The estimated fission neutron yield must be either zero "
869 "or in the range (1.0, 10.0).");
870 }
871
872 ChiLogicalErrorIf(sigma_f_.empty(), "After reading xs, a fissionable "
873 "material's sigma_f is not defined");
874 }//if fissionable
875}
#define ChiLogicalErrorIf(condition, message)
static chi::ChiLog & log
Definition: chi_runtime.h:81
LogStream Log0Warning()
Definition: chi_log.h:231
LogStream Log(LOG_LVL level=LOG_0)
Definition: chi_log.cc:35
unsigned int scattering_order_
Legendre scattering order.
std::vector< double > sigma_f_
Fission cross section.
std::vector< std::vector< double > > production_matrix_
std::vector< double > nu_prompt_sigma_f_
unsigned int num_groups_
Total number of groups.
std::vector< std::vector< double > > e_bounds_
Energy bin boundaries in MeV.
unsigned int num_precursors_
Number of precursors.
std::vector< double > sigma_t_
Total cross section.
std::vector< chi_math::SparseMatrix > transfer_matrices_
std::vector< double > inv_velocity_
std::vector< Precursor > precursors_
std::vector< double > nu_sigma_f_
std::vector< double > nu_delayed_sigma_f_
void MakeFromChiXSFile(const std::string &file_name)
std::vector< double > sigma_a_
Absorption cross section.