@@ -369,11 +369,20 @@ def applyMaterialMassFracsToNumberDensities(self):
369
369
# `density` is 3D density
370
370
# call getProperty to cache and improve speed
371
371
density = self .material .getProperty ("pseudoDensity" , Tc = self .temperatureInC )
372
-
373
372
self .p .numberDensities = densityTools .getNDensFromMasses (
374
373
density , self .material .massFrac
375
374
)
376
375
376
+ # Sometimes the material thermal expansion depends on its parents composition (eg Pu frac) so
377
+ # setting number densities can sometimes change thermal expansion behavior.
378
+ # so call again so that the material has access to its parents comp when providing the reference initial density.
379
+ densityBasedOnParentComposition = self .material .getProperty (
380
+ "pseudoDensity" , Tc = self .temperatureInC
381
+ )
382
+ self .p .numberDensities = densityTools .getNDensFromMasses (
383
+ densityBasedOnParentComposition , self .material .massFrac
384
+ )
385
+
377
386
# material needs to be expanded from the material's cold temp to hot,
378
387
# not components cold temp, so we don't use mat.linearExpansionFactor or
379
388
# component.getThermalExpansionFactor.
@@ -694,7 +703,7 @@ def setPuFrac(self, prevPuFrac):
694
703
dLLprev = prevMaterial .linearExpansionPercent (Tc = self .temperatureInC ) / 100.0
695
704
dLLnew = self .material .linearExpansionPercent (Tc = self .temperatureInC ) / 100.0
696
705
expansionRatio = (1.0 + dLLnew ) / (1.0 + dLLprev )
697
- f = 1.0 / expansionRatio ** 2
706
+ f = 1.0 / expansionRatio ** 2
698
707
self .changeNDensByFactor (f )
699
708
self .clearLinkedCache ()
700
709
@@ -753,12 +762,7 @@ def setNumberDensity(self, nucName, val):
753
762
val : float
754
763
Number density to set in atoms/bn-cm (heterogeneous)
755
764
"""
756
- self .p .numberDensities [nucName ] = val
757
- self .p .assigned = parameters .SINCE_ANYTHING
758
- # necessary for syncMpiState
759
- parameters .ALL_DEFINITIONS [
760
- "numberDensities"
761
- ].assigned = parameters .SINCE_ANYTHING
765
+ self .updateNumberDensities ({nucName : val })
762
766
763
767
def setNumberDensities (self , numberDensities ):
764
768
"""
@@ -777,12 +781,18 @@ def setNumberDensities(self, numberDensities):
777
781
numberDensities : dict
778
782
nucName: ndens pairs.
779
783
780
- Notes
781
- -----
782
- We don't just call setNumberDensity for each nuclide because we don't want to call ``getVolumeFractions``
783
- for each nuclide (it's inefficient).
784
+ Note that sometimes volume/dimensions can change due to the number density change when the material thermal
785
+ expansion depends on the component's composition (eg its plutonium fraction). In this case, changing the
786
+ density will implicitly change the area/volume. Since it its very difficult to predict the new dims ahead of time,
787
+ and perturbation/depletion calculations are almost exclusively done assuming constant volume,
788
+ the densities sent are automatically perturbed to conserve mass with the original dimensions.
789
+ That is, the components densities are not exactly as passed, but whatever they would need to be to preserve volume
790
+ integrated number densities (moles) from the pre-perturbed components volume/dims.
791
+ Note this has no effect if the material thermal expansion has no dependence on component composition fracs.
792
+ If this is not desired, `self.p.numberDensities` can be set directly.
784
793
"""
785
- self .p .numberDensities = numberDensities
794
+ self .p .numberDensities = {} # clear things not passed
795
+ self .updateNumberDensities (numberDensities )
786
796
787
797
def updateNumberDensities (self , numberDensities ):
788
798
"""
@@ -793,9 +803,48 @@ def updateNumberDensities(self, numberDensities):
793
803
numberDensities : dict
794
804
nucName: ndens pairs.
795
805
806
+ Notes
807
+ -----
808
+ Note that sometimes volume/dimensions can change due to the number density change when the material thermal
809
+ expansion depends on the component's composition (eg its plutonium fraction). In this case, changing the
810
+ density will implicitly change the area/volume. Since it its very difficult to predict the new dims ahead of time,
811
+ and perturbation/depletion calculations are almost exclusively done assuming constant volume,
812
+ the densities sent are automatically perturbed to conserve mass with the original dimensions.
813
+ That is, the components densities are not exactly as passed, but whatever they would need to be to preserve volume
814
+ integrated number densities (moles) from the pre-perturbed components volume/dims.
815
+ Note this has no effect if the material thermal expansion has no dependence on component composition fracs.
816
+ If this is not desired, `self.p.numberDensities` can be set directly.
796
817
"""
818
+ # prepare to change the densities with knowledge that dims could change due to
819
+ # material thermal expansion dependence on composition
820
+ dLLprev = self .material .linearExpansionPercent (Tc = self .temperatureInC ) / 100.0
821
+ try :
822
+ vol = self .getVolume ()
823
+ except :
824
+ # either no parent to get height or parent's height is None
825
+ # which would be AttributeError and TypeError respectively, but other errors could be possible
826
+ vol = None
827
+ area = self .getArea ()
828
+
829
+ # change the densities
797
830
self .p .numberDensities .update (numberDensities )
798
- # since we're updating the object the param points to but not the param itself, we have to inform
831
+
832
+ # check if thermal expansion changed
833
+ dLLnew = self .material .linearExpansionPercent (Tc = self .temperatureInC ) / 100.0
834
+ if dLLprev != dLLnew :
835
+ # the thermal expansion changed so the volume change is happening at same time as
836
+ # density change was requested. Attempt to make mass consistent with old dims
837
+ # (since the density change was for the old volume and otherwise mass wouldn't be conserved)
838
+
839
+ # enable recalculation of volume, otherwise it uses stored.
840
+ self .clearLinkedCache ()
841
+ if vol is not None :
842
+ factor = vol / self .getVolume ()
843
+ else :
844
+ factor = area / self .getArea ()
845
+ self .changeNDensByFactor (factor )
846
+
847
+ # since we're calling update on the object the param points to, but not the param itself, we have to inform
799
848
# the param system to flag it as modified so it properly syncs during ``syncMpiState``.
800
849
self .p .assigned = parameters .SINCE_ANYTHING
801
850
self .p .paramDefs ["numberDensities" ].assigned = parameters .SINCE_ANYTHING
0 commit comments