From 09143db02fced08fa777b3b1962611e943576324 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 11:00:59 +0300 Subject: [PATCH 01/11] save reference direction for azimutal angle --- include/openmc/distribution_multi.h | 2 ++ openmc/stats/multivariate.py | 23 ++++++++++++++++++++++- src/distribution_multi.cpp | 19 ++++++++++++++++--- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/include/openmc/distribution_multi.h b/include/openmc/distribution_multi.h index 9e84d03d57f..75126593f7d 100644 --- a/include/openmc/distribution_multi.h +++ b/include/openmc/distribution_multi.h @@ -51,6 +51,8 @@ class PolarAzimuthal : public UnitSphereDistribution { Distribution* phi() const { return phi_.get(); } private: + Direction v_ref_ {1.0, 0.0, 0.0}; //!< reference direction + Direction w_ref_; UPtrDist mu_; //!< Distribution of polar angle UPtrDist phi_; //!< Distribution of azimuthal angle }; diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index 222d2d18a56..e6729dd3c8b 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -79,6 +79,9 @@ class PolarAzimuthal(UnitSphere): reference_uvw : Iterable of float Direction from which polar angle is measured. Defaults to the positive z-direction. + reference_vwu : Iterable of float + Direction from which azimuthal angle is measured. Defaults to the positive + x-direction. Attributes ---------- @@ -89,8 +92,9 @@ class PolarAzimuthal(UnitSphere): """ - def __init__(self, mu=None, phi=None, reference_uvw=(0., 0., 1.)): + def __init__(self, mu=None, phi=None, reference_uvw=(0., 0., 1.), reference_vwu=(1., 0., 0.)): super().__init__(reference_uvw) + self.reference_vwu = reference_vwu if mu is not None: self.mu = mu else: @@ -100,6 +104,18 @@ def __init__(self, mu=None, phi=None, reference_uvw=(0., 0., 1.)): self.phi = phi else: self.phi = Uniform(0., 2*pi) + + @property + def reference_vwu(self): + return self._reference_vwu + + @reference_vwu.setter + def reference_vwu(self, vwu): + cv.check_type('reference v direction', vwu, Iterable, Real) + cv.check_less_than('reference v direction must be orthogonal to reference u direction', vwu.dot(uvw), 1e-6) + vwu = np.asarray(vwu) + vwu -= vwu.dot(uvw)*uvw + self._reference_vwu = vwu/np.linalg.norm(vwu) @property def mu(self): @@ -132,6 +148,8 @@ def to_xml_element(self): element.set("type", "mu-phi") if self.reference_uvw is not None: element.set("reference_uvw", ' '.join(map(str, self.reference_uvw))) + if self.reference_vwu is not None: + element.set("reference_vwu", ' '.join(map(str, self.reference_uwu))) element.append(self.mu.to_xml_element('mu')) element.append(self.phi.to_xml_element('phi')) return element @@ -155,6 +173,9 @@ def from_xml_element(cls, elem): uvw = get_elem_list(elem, "reference_uvw", float) if uvw is not None: mu_phi.reference_uvw = uvw + vwu = get_elem_list(elem, "reference_vwu", float) + if vwu is not None: + mu_phi.reference_vwu = vwu mu_phi.mu = Univariate.from_xml_element(elem.find('mu')) mu_phi.phi = Univariate.from_xml_element(elem.find('phi')) return mu_phi diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index b7b3efe5268..3f826bb5f6d 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -58,6 +58,15 @@ PolarAzimuthal::PolarAzimuthal(Direction u, UPtrDist mu, UPtrDist phi) PolarAzimuthal::PolarAzimuthal(pugi::xml_node node) : UnitSphereDistribution {node} { + // Read reference directional unit vector + if (check_for_node(node, "reference_vwu")) { + auto v_ref = get_node_array(node, "reference_vwu"); + if (v_ref.size() != 3) + fatal_error("Angular distribution reference v direction must have " + "three parameters specified."); + v_ref_ = Direction(v_ref.data()); + } + w_ref_ = u_ref_.cross(v_ref_); if (check_for_node(node, "mu")) { pugi::xml_node node_dist = node.child("mu"); mu_ = distribution_from_xml(node_dist); @@ -79,11 +88,15 @@ Direction PolarAzimuthal::sample(uint64_t* seed) const double mu = mu_->sample(seed); if (mu == 1.0) return u_ref_; - + if (mu == -1.0) + return -u_ref_; + // Sample azimuthal angle double phi = phi_->sample(seed); - - return rotate_angle(u_ref_, mu, &phi, seed); + + double f = std::sqrt(1-mu*mu); + + return mu*u_ref_ + f*std::cos(phi)*v_ref_ + f*std::sin(phi)*w_ref_; } //============================================================================== From ad8a276ddf9867fd15e8eb5f58c380c6419140c4 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 11:06:19 +0300 Subject: [PATCH 02/11] ran clang format --- openmc/stats/multivariate.py | 238 +++++++++++++++++------------------ src/distribution_multi.cpp | 10 +- 2 files changed, 119 insertions(+), 129 deletions(-) diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index e6729dd3c8b..ce049e9dded 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -442,129 +442,119 @@ class SphericalIndependent(Spatial): Distribution of phi-coordinates (azimuthal angle) in the local reference frame origin: Iterable of float, optional - coordinates (x0, y0, z0) of the center of the spherical reference - frame. Defaults to (0.0, 0.0, 0.0) - - """ - - def __init__(self, r, cos_theta, phi, origin=(0.0, 0.0, 0.0)): - self.r = r - self.cos_theta = cos_theta - self.phi = phi - self.origin = origin - - @property - def r(self): - return self._r - - @r.setter - def r(self, r): - cv.check_type('r coordinate', r, Univariate) - self._r = r - - @property - def cos_theta(self): - return self._cos_theta - - @cos_theta.setter - def cos_theta(self, cos_theta): - cv.check_type('cos_theta coordinate', cos_theta, Univariate) - self._cos_theta = cos_theta - - @property - def phi(self): - return self._phi - - @phi.setter - def phi(self, phi): - cv.check_type('phi coordinate', phi, Univariate) - self._phi = phi - - @property - def origin(self): - return self._origin - - @origin.setter - def origin(self, origin): - cv.check_type('origin coordinates', origin, Iterable, Real) - origin = np.asarray(origin) - self._origin = origin - - def to_xml_element(self): - """Return XML representation of the spatial distribution - - Returns - ------- - element : lxml.etree._Element - XML element containing spatial distribution data - - """ - element = ET.Element('space') - element.set('type', 'spherical') - element.append(self.r.to_xml_element('r')) - element.append(self.cos_theta.to_xml_element('cos_theta')) - element.append(self.phi.to_xml_element('phi')) - element.set("origin", ' '.join(map(str, self.origin))) - return element - - @classmethod - def from_xml_element(cls, elem: ET.Element): - """Generate spatial distribution from an XML element - - Parameters - ---------- - elem : lxml.etree._Element - XML element - - Returns - ------- - openmc.stats.SphericalIndependent - Spatial distribution generated from XML element - - """ - r = Univariate.from_xml_element(elem.find('r')) - cos_theta = Univariate.from_xml_element(elem.find('cos_theta')) - phi = Univariate.from_xml_element(elem.find('phi')) - origin = get_elem_list(elem, "origin", float) - return cls(r, cos_theta, phi, origin=origin) - - -class CylindricalIndependent(Spatial): - r"""Spatial distribution represented in cylindrical coordinates. - - This distribution allows one to specify coordinates whose :math:`r`, - :math:`\phi`, and :math:`z` components are sampled independently from - one another and in a reference frame whose origin is specified by the - coordinates (x0, y0, z0). - - .. versionadded:: 0.12 - - Parameters - ---------- - r : openmc.stats.Univariate - Distribution of r-coordinates in a reference frame specified by the - origin parameter - phi : openmc.stats.Univariate - Distribution of phi-coordinates (azimuthal angle) in a reference frame - specified by the origin parameter - z : openmc.stats.Univariate - Distribution of z-coordinates in a reference frame specified by the - origin parameter - origin: Iterable of float, optional - coordinates (x0, y0, z0) of the center of the cylindrical reference - frame. Defaults to (0.0, 0.0, 0.0) - - Attributes - ---------- - r : openmc.stats.Univariate - Distribution of r-coordinates in the local reference frame - phi : openmc.stats.Univariate - Distribution of phi-coordinates (azimuthal angle) in the local - reference frame - z : openmc.stats.Univariate - Distribution of z-coordinates in the local reference frame - origin: Iterable of float, optional - coordinates (x0, y0, z0) of the center of the cylindrical reference + coordinates (x0, y0, z0) +of the center of the spherical reference frame + .Defaults to(0.0, 0.0, 0.0) + + "" + " + + def __init__(self, r, cos_theta, phi, origin = (0.0, 0.0, 0.0)) + : self.r = r self.cos_theta = cos_theta self.phi = phi self.origin = + origin + + @property def r(self) + : return self + ._r + + @r.setter def r(self, r) + : cv.check_type('r coordinate', r, Univariate) self._r = + r + + @property def cos_theta(self) + : return self + ._cos_theta + + @cos_theta.setter def cos_theta(self, cos_theta) + : cv.check_type('cos_theta coordinate', cos_theta, + Univariate) self._cos_theta = cos_theta + + @property def phi(self) + : return self + ._phi + + @phi.setter def phi(self, phi) + : cv.check_type('phi coordinate', phi, Univariate) self._phi = + phi + + @property def origin(self) + : return self + ._origin + + @origin.setter def origin(self, origin) + : cv.check_type('origin coordinates', origin, Iterable, Real) origin = + np.asarray(origin) self._origin = origin + + def to_xml_element(self) + : "" + "Return XML representation of the spatial distribution + + Returns-- -- -- -element + : lxml.etree._Element XML element containing spatial distribution data + + "" + " + element = ET.Element('space') element.set('type', 'spherical') element + .append(self.r.to_xml_element('r')) element + .append(self.cos_theta.to_xml_element('cos_theta')) element + .append(self.phi.to_xml_element('phi')) element + .set("origin", ' '.join(map(str, self.origin))) return element + + @classmethod def from_xml_element(cls, elem + : ET.Element) + : "" + "Generate spatial distribution from an XML element + + Parameters-- -- -- -- --elem + : lxml.etree._Element XML element + + Returns-- -- -- -openmc.stats.SphericalIndependent Spatial + distribution generated from XML element + + "" + " + r = Univariate.from_xml_element(elem.find('r')) cos_theta = + Univariate.from_xml_element(elem.find('cos_theta')) phi = + Univariate.from_xml_element(elem.find('phi')) origin = + get_elem_list(elem, "origin", float) return cls( + r, cos_theta, phi, origin = origin) + + class CylindricalIndependent(Spatial) + : r "" + "Spatial distribution represented in cylindrical coordinates. + + This distribution allows one to specify coordinates whose : math :`r`, + : math :`\phi`, + and : math :`z` components are sampled independently from one another and in + a reference frame whose origin is specified by the coordinates(x0, y0, z0) + . + + . + .versionadded::0.12 + + Parameters-- -- -- -- --r : openmc.stats.Univariate Distribution of r + - + coordinates in a reference frame specified by the origin parameter phi + : openmc.stats.Univariate Distribution of phi + - + coordinates(azimuthal angle) in a reference frame specified by the origin + parameter z : openmc.stats.Univariate Distribution of z + - + coordinates in a reference frame specified by the origin parameter origin + : Iterable of float, + optional coordinates(x0, y0, z0) +of the center of the cylindrical reference frame + .Defaults to(0.0, 0.0, 0.0) + + Attributes-- -- -- -- --r : openmc.stats.Univariate Distribution of r + - + coordinates in the local reference frame phi + : openmc.stats.Univariate Distribution of phi + - + coordinates(azimuthal angle) in the local reference frame z + : openmc.stats.Univariate Distribution of z + - coordinates in the local reference frame origin : Iterable of float, + optional coordinates(x0, y0, z0) of the center of the cylindrical reference frame. Defaults to (0.0, 0.0, 0.0) """ @@ -766,7 +756,7 @@ def from_xml_element(cls, elem, meshes): mesh_id = int(get_text(elem, "mesh_id")) - # check if this mesh has been read in from another location already +#check if this mesh has been read in from another location already if mesh_id not in meshes: raise ValueError(f'Could not locate mesh with ID "{mesh_id}"') diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index 3f826bb5f6d..cdb33adc2ad 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -90,13 +90,13 @@ Direction PolarAzimuthal::sample(uint64_t* seed) const return u_ref_; if (mu == -1.0) return -u_ref_; - + // Sample azimuthal angle double phi = phi_->sample(seed); - - double f = std::sqrt(1-mu*mu); - - return mu*u_ref_ + f*std::cos(phi)*v_ref_ + f*std::sin(phi)*w_ref_; + + double f = std::sqrt(1 - mu * mu); + + return mu * u_ref_ + f * std::cos(phi) * v_ref_ + f * std::sin(phi) * w_ref_; } //============================================================================== From 717c4d97453a15c863b4c2b785a6da037c5f1766 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 11:08:45 +0300 Subject: [PATCH 03/11] revert ran clang format on python file --- openmc/stats/multivariate.py | 238 ++++++++++++++++++----------------- 1 file changed, 124 insertions(+), 114 deletions(-) diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index ce049e9dded..e6729dd3c8b 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -442,119 +442,129 @@ class SphericalIndependent(Spatial): Distribution of phi-coordinates (azimuthal angle) in the local reference frame origin: Iterable of float, optional - coordinates (x0, y0, z0) -of the center of the spherical reference frame - .Defaults to(0.0, 0.0, 0.0) - - "" - " - - def __init__(self, r, cos_theta, phi, origin = (0.0, 0.0, 0.0)) - : self.r = r self.cos_theta = cos_theta self.phi = phi self.origin = - origin - - @property def r(self) - : return self - ._r - - @r.setter def r(self, r) - : cv.check_type('r coordinate', r, Univariate) self._r = - r - - @property def cos_theta(self) - : return self - ._cos_theta - - @cos_theta.setter def cos_theta(self, cos_theta) - : cv.check_type('cos_theta coordinate', cos_theta, - Univariate) self._cos_theta = cos_theta - - @property def phi(self) - : return self - ._phi - - @phi.setter def phi(self, phi) - : cv.check_type('phi coordinate', phi, Univariate) self._phi = - phi - - @property def origin(self) - : return self - ._origin - - @origin.setter def origin(self, origin) - : cv.check_type('origin coordinates', origin, Iterable, Real) origin = - np.asarray(origin) self._origin = origin - - def to_xml_element(self) - : "" - "Return XML representation of the spatial distribution - - Returns-- -- -- -element - : lxml.etree._Element XML element containing spatial distribution data - - "" - " - element = ET.Element('space') element.set('type', 'spherical') element - .append(self.r.to_xml_element('r')) element - .append(self.cos_theta.to_xml_element('cos_theta')) element - .append(self.phi.to_xml_element('phi')) element - .set("origin", ' '.join(map(str, self.origin))) return element - - @classmethod def from_xml_element(cls, elem - : ET.Element) - : "" - "Generate spatial distribution from an XML element - - Parameters-- -- -- -- --elem - : lxml.etree._Element XML element - - Returns-- -- -- -openmc.stats.SphericalIndependent Spatial - distribution generated from XML element - - "" - " - r = Univariate.from_xml_element(elem.find('r')) cos_theta = - Univariate.from_xml_element(elem.find('cos_theta')) phi = - Univariate.from_xml_element(elem.find('phi')) origin = - get_elem_list(elem, "origin", float) return cls( - r, cos_theta, phi, origin = origin) - - class CylindricalIndependent(Spatial) - : r "" - "Spatial distribution represented in cylindrical coordinates. - - This distribution allows one to specify coordinates whose : math :`r`, - : math :`\phi`, - and : math :`z` components are sampled independently from one another and in - a reference frame whose origin is specified by the coordinates(x0, y0, z0) - . - - . - .versionadded::0.12 - - Parameters-- -- -- -- --r : openmc.stats.Univariate Distribution of r - - - coordinates in a reference frame specified by the origin parameter phi - : openmc.stats.Univariate Distribution of phi - - - coordinates(azimuthal angle) in a reference frame specified by the origin - parameter z : openmc.stats.Univariate Distribution of z - - - coordinates in a reference frame specified by the origin parameter origin - : Iterable of float, - optional coordinates(x0, y0, z0) -of the center of the cylindrical reference frame - .Defaults to(0.0, 0.0, 0.0) - - Attributes-- -- -- -- --r : openmc.stats.Univariate Distribution of r - - - coordinates in the local reference frame phi - : openmc.stats.Univariate Distribution of phi - - - coordinates(azimuthal angle) in the local reference frame z - : openmc.stats.Univariate Distribution of z - - coordinates in the local reference frame origin : Iterable of float, - optional coordinates(x0, y0, z0) of the center of the cylindrical reference + coordinates (x0, y0, z0) of the center of the spherical reference + frame. Defaults to (0.0, 0.0, 0.0) + + """ + + def __init__(self, r, cos_theta, phi, origin=(0.0, 0.0, 0.0)): + self.r = r + self.cos_theta = cos_theta + self.phi = phi + self.origin = origin + + @property + def r(self): + return self._r + + @r.setter + def r(self, r): + cv.check_type('r coordinate', r, Univariate) + self._r = r + + @property + def cos_theta(self): + return self._cos_theta + + @cos_theta.setter + def cos_theta(self, cos_theta): + cv.check_type('cos_theta coordinate', cos_theta, Univariate) + self._cos_theta = cos_theta + + @property + def phi(self): + return self._phi + + @phi.setter + def phi(self, phi): + cv.check_type('phi coordinate', phi, Univariate) + self._phi = phi + + @property + def origin(self): + return self._origin + + @origin.setter + def origin(self, origin): + cv.check_type('origin coordinates', origin, Iterable, Real) + origin = np.asarray(origin) + self._origin = origin + + def to_xml_element(self): + """Return XML representation of the spatial distribution + + Returns + ------- + element : lxml.etree._Element + XML element containing spatial distribution data + + """ + element = ET.Element('space') + element.set('type', 'spherical') + element.append(self.r.to_xml_element('r')) + element.append(self.cos_theta.to_xml_element('cos_theta')) + element.append(self.phi.to_xml_element('phi')) + element.set("origin", ' '.join(map(str, self.origin))) + return element + + @classmethod + def from_xml_element(cls, elem: ET.Element): + """Generate spatial distribution from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + + Returns + ------- + openmc.stats.SphericalIndependent + Spatial distribution generated from XML element + + """ + r = Univariate.from_xml_element(elem.find('r')) + cos_theta = Univariate.from_xml_element(elem.find('cos_theta')) + phi = Univariate.from_xml_element(elem.find('phi')) + origin = get_elem_list(elem, "origin", float) + return cls(r, cos_theta, phi, origin=origin) + + +class CylindricalIndependent(Spatial): + r"""Spatial distribution represented in cylindrical coordinates. + + This distribution allows one to specify coordinates whose :math:`r`, + :math:`\phi`, and :math:`z` components are sampled independently from + one another and in a reference frame whose origin is specified by the + coordinates (x0, y0, z0). + + .. versionadded:: 0.12 + + Parameters + ---------- + r : openmc.stats.Univariate + Distribution of r-coordinates in a reference frame specified by the + origin parameter + phi : openmc.stats.Univariate + Distribution of phi-coordinates (azimuthal angle) in a reference frame + specified by the origin parameter + z : openmc.stats.Univariate + Distribution of z-coordinates in a reference frame specified by the + origin parameter + origin: Iterable of float, optional + coordinates (x0, y0, z0) of the center of the cylindrical reference + frame. Defaults to (0.0, 0.0, 0.0) + + Attributes + ---------- + r : openmc.stats.Univariate + Distribution of r-coordinates in the local reference frame + phi : openmc.stats.Univariate + Distribution of phi-coordinates (azimuthal angle) in the local + reference frame + z : openmc.stats.Univariate + Distribution of z-coordinates in the local reference frame + origin: Iterable of float, optional + coordinates (x0, y0, z0) of the center of the cylindrical reference frame. Defaults to (0.0, 0.0, 0.0) """ @@ -756,7 +766,7 @@ def from_xml_element(cls, elem, meshes): mesh_id = int(get_text(elem, "mesh_id")) -#check if this mesh has been read in from another location already + # check if this mesh has been read in from another location already if mesh_id not in meshes: raise ValueError(f'Could not locate mesh with ID "{mesh_id}"') From fe7a80e31bb878377f48b08eced2de4f6ee7a8f9 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 11:26:21 +0300 Subject: [PATCH 04/11] bugfix --- openmc/stats/multivariate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index e6729dd3c8b..3ec148fdecf 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -112,8 +112,9 @@ def reference_vwu(self): @reference_vwu.setter def reference_vwu(self, vwu): cv.check_type('reference v direction', vwu, Iterable, Real) - cv.check_less_than('reference v direction must be orthogonal to reference u direction', vwu.dot(uvw), 1e-6) vwu = np.asarray(vwu) + uvw = self.reference_uvw + cv.check_less_than('reference v direction must be orthogonal to reference u direction', vwu.dot(uvw), 1e-6) vwu -= vwu.dot(uvw)*uvw self._reference_vwu = vwu/np.linalg.norm(vwu) From 12d18161030554762883e111e3512e1e3effd32a Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 11:28:50 +0300 Subject: [PATCH 05/11] bugfix --- openmc/stats/multivariate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index 3ec148fdecf..421db6d53a5 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -150,7 +150,7 @@ def to_xml_element(self): if self.reference_uvw is not None: element.set("reference_uvw", ' '.join(map(str, self.reference_uvw))) if self.reference_vwu is not None: - element.set("reference_vwu", ' '.join(map(str, self.reference_uwu))) + element.set("reference_vwu", ' '.join(map(str, self.reference_vwu))) element.append(self.mu.to_xml_element('mu')) element.append(self.phi.to_xml_element('phi')) return element From 85b5d3cb019d6ec01e90b81065bcb8327fcde78f Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 12:38:02 +0300 Subject: [PATCH 06/11] update input --- tests/regression_tests/source/inputs_true.dat | 2 +- tests/regression_tests/source/results_true.dat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression_tests/source/inputs_true.dat b/tests/regression_tests/source/inputs_true.dat index 9f10b79d6b1..0c3764ba6f9 100644 --- a/tests/regression_tests/source/inputs_true.dat +++ b/tests/regression_tests/source/inputs_true.dat @@ -25,7 +25,7 @@ -2.0 0.0 2.0 0.2 0.3 0.2 - + -1.0 0.0 1.0 0.5 0.25 0.25 diff --git a/tests/regression_tests/source/results_true.dat b/tests/regression_tests/source/results_true.dat index 951075bbb90..7d03c696d3e 100644 --- a/tests/regression_tests/source/results_true.dat +++ b/tests/regression_tests/source/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.034717E-01 2.799386E-03 +3.080655E-01 4.837707E-03 From 51fffc5bc6cf8301fd9dda018bcc9c937aedd7c6 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 12:52:49 +0300 Subject: [PATCH 07/11] fix regression test --- src/distribution_multi.cpp | 2 -- tests/regression_tests/source/results_true.dat | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index cdb33adc2ad..d02059d89a5 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -88,8 +88,6 @@ Direction PolarAzimuthal::sample(uint64_t* seed) const double mu = mu_->sample(seed); if (mu == 1.0) return u_ref_; - if (mu == -1.0) - return -u_ref_; // Sample azimuthal angle double phi = phi_->sample(seed); diff --git a/tests/regression_tests/source/results_true.dat b/tests/regression_tests/source/results_true.dat index 7d03c696d3e..951075bbb90 100644 --- a/tests/regression_tests/source/results_true.dat +++ b/tests/regression_tests/source/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.080655E-01 4.837707E-03 +3.034717E-01 2.799386E-03 From 82c4ac66eda48fbd503a5ade5de7b01a7492fa53 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 12:56:02 +0300 Subject: [PATCH 08/11] add case for mu=-1 that change regression --- src/distribution_multi.cpp | 4 +++- tests/regression_tests/source/results_true.dat | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index d02059d89a5..09822e31e51 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -88,7 +88,9 @@ Direction PolarAzimuthal::sample(uint64_t* seed) const double mu = mu_->sample(seed); if (mu == 1.0) return u_ref_; - + if (mu == -1.0) + return -u_ref_; + // Sample azimuthal angle double phi = phi_->sample(seed); diff --git a/tests/regression_tests/source/results_true.dat b/tests/regression_tests/source/results_true.dat index 951075bbb90..7d03c696d3e 100644 --- a/tests/regression_tests/source/results_true.dat +++ b/tests/regression_tests/source/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.034717E-01 2.799386E-03 +3.080655E-01 4.837707E-03 From ff7aeea5b6fec961b551d2dfe8181ef39fd22a2c Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 25 Sep 2025 12:56:32 +0300 Subject: [PATCH 09/11] ran clang format --- src/distribution_multi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index 09822e31e51..cdb33adc2ad 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -90,7 +90,7 @@ Direction PolarAzimuthal::sample(uint64_t* seed) const return u_ref_; if (mu == -1.0) return -u_ref_; - + // Sample azimuthal angle double phi = phi_->sample(seed); From c10a674b35f6fdc1f78981879908fc4a20a5ab1c Mon Sep 17 00:00:00 2001 From: shimwell Date: Fri, 31 Oct 2025 13:58:34 +0100 Subject: [PATCH 10/11] added ref vwu tests --- tests/unit_tests/test_stats.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/unit_tests/test_stats.py b/tests/unit_tests/test_stats.py index abf143f12a2..998d4b984ce 100644 --- a/tests/unit_tests/test_stats.py +++ b/tests/unit_tests/test_stats.py @@ -516,3 +516,33 @@ def test_combine_distributions(): # uncertainty of the expected value samples = combined.sample(10_000) assert_sample_mean(samples, 0.25) + +def test_reference_vwu_projection(): + """When a non-orthogonal vector is provided, the setter should project out + any component along reference_uvw so the stored vector is orthogonal. + """ + pa = openmc.stats.PolarAzimuthal() # default reference_uvw == (0, 0, 1) + + # Provide a vector that is not orthogonal to (0,0,1) + pa.reference_vwu = (2.0, 0.5, 0.3) + + reference_v = np.asarray(pa.reference_vwu) + reference_u = np.asarray(pa.reference_uvw) + + # reference_v should be orthogonal to reference_u + assert abs(np.dot(reference_v, reference_u)) < 1e-6 + + +def test_reference_vwu_normalization(): + """When a non-normalized vector is provided, the setter should normalize + the projected vector to unit length. + """ + pa = openmc.stats.PolarAzimuthal() # default reference_uvw == (0, 0, 1) + + # Provide a vector that is neither orthogonal to (0,0,1) nor unit-length + pa.reference_vwu = (2.0, 0.5, 0.3) + + reference_v = np.asarray(pa.reference_vwu) + + # reference_v should be unit length + assert np.isclose(np.linalg.norm(reference_v), 1.0, atol=1e-12) From b242c32e56f32168a80526b3c76ded871b35b1cb Mon Sep 17 00:00:00 2001 From: GuySten Date: Sun, 2 Nov 2025 08:14:32 +0200 Subject: [PATCH 11/11] fixed checks in PolarAzimuthal --- openmc/stats/multivariate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index 421db6d53a5..1ce998758ad 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -114,8 +114,9 @@ def reference_vwu(self, vwu): cv.check_type('reference v direction', vwu, Iterable, Real) vwu = np.asarray(vwu) uvw = self.reference_uvw - cv.check_less_than('reference v direction must be orthogonal to reference u direction', vwu.dot(uvw), 1e-6) - vwu -= vwu.dot(uvw)*uvw + cv.check_greater_than('reference v direction must not be parallel to reference u direction', np.linalg.norm(np.cross(vwu,uvw)), 1e-6*np.linalg.norm(vwu)) + vwu -= vwu.dot(uvw)*uvw + cv.check_less_than('reference v direction must be orthogonal to reference u direction', np.abs(vwu.dot(uvw)), 1e-6) self._reference_vwu = vwu/np.linalg.norm(vwu) @property