-
Notifications
You must be signed in to change notification settings - Fork 525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementation of Triply Periodic Minimal Surfaces (TPMS) in OpenMC #3292
base: develop
Are you sure you want to change the base?
Changes from all commits
194c53a
cad5288
34dfcbc
aba087b
4b64056
d135cc9
ff3c463
c01af03
5456834
d737e1a
1baeb79
0e89a7a
e3a9932
887929a
5e110f2
d68db81
c7c1d02
dd4d98f
fe2018b
c730d3a
23f626b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,3 +106,6 @@ CMakeSettings.json | |
|
||
# Python pickle files | ||
*.pkl | ||
|
||
# Validation | ||
validation |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,67 @@ | ||
# TPMS branch specific (INL developers) | ||
|
||
<img src="https://media1.tenor.com/m/98dCgWDs4rsAAAAC/epic-sax-guy-sax.gif" width="100px" align="center"> | ||
|
||
## Installation | ||
|
||
##### create a new conda env: | ||
``` | ||
conda create -n openmc-TPMS moose-dev=2024.10.01=mpich | ||
conda deactivate | ||
conda activate openmc-TPMS | ||
# conda install boost=1.85.0 # not needed anymore | ||
``` | ||
##### clone the project to your local repo: | ||
``` | ||
git clone [email protected]:paul-ferney/openmc.git | ||
cd openmc | ||
git checkout TPMS | ||
``` | ||
##### install openmc python API in dev mode: | ||
``` | ||
python -m pip install -e .[test] | ||
``` | ||
##### check that your compilers are from your conda env: | ||
``` | ||
which $CC && which $CXX | ||
>>> /opt/anaconda3/envs/openmc-TPMS/bin/mpicc | ||
>>> /opt/anaconda3/envs/openmc-TPMS/bin/mpicxx | ||
``` | ||
##### create a build directory and make your project: | ||
``` | ||
mkdir ./build && cd ./build | ||
cmake .. && make -j 12 | ||
cd .. | ||
``` | ||
##### test openmc: | ||
``` | ||
cd tests/regression_tests/TPMS/base_no_tpms | ||
python base_no_tpms.py | ||
../../../build/bin/openmc | ||
``` | ||
|
||
## Trouble shooting | ||
### Shared libraries conflict | ||
When running cmake, you might get: | ||
Warning Cannot generate a safe runtime search path for target libopenmc because files in some directories may conflict with libraries in implicit directories: | ||
``` | ||
CMake Warning at CMakeLists.txt:463 (add_library): | ||
Cannot generate a safe runtime search path for target libopenmc because | ||
files in some directories may conflict with libraries in implicit | ||
directories: | ||
|
||
runtime library [libpng.dylib] in /opt/anaconda3/envs/openmc-TPMS/lib may be hidden by files in: | ||
/opt/homebrew/lib | ||
runtime library [libpugixml.1.dylib] in /opt/anaconda3/envs/openmc-TPMS/lib may be hidden by files in: | ||
/opt/homebrew/lib | ||
|
||
Some of these libraries may not be found correctly. | ||
``` | ||
There is a conflict between your conda env shared libraries and shared libraries installed somewhere else. You want cmake to strictly use your conda libs. Run instead: | ||
``` | ||
cmake .. -DCMAKE_PREFIX_PATH=/opt/anaconda3/envs/openmc-TPMS/ -DOPENMC_USE_MPI=ON -DCMAKE_FIND_USE_CMAKE_SYSTEM_PATH=FALSE -DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH=FALSE | ||
``` | ||
|
||
# OpenMC Monte Carlo Particle Transport Code | ||
|
||
[](https://docs.openmc.org/en/latest/license.html) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// taken fom boost1.85 - boost::math::tools::bisect | ||
// | ||
// Boost Software License - Version 1.0 - August 17th, 2003 | ||
|
||
// Permission is hereby granted, free of charge, to any person or organization | ||
// obtaining a copy of the software and accompanying documentation covered by | ||
// this license (the "Software") to use, reproduce, display, distribute, | ||
// execute, and transmit the Software, and to prepare derivative works of the | ||
// Software, and to permit third-parties to whom the Software is furnished to | ||
// do so, all subject to the following: | ||
// | ||
// The copyright notices in the Software and this entire statement, including | ||
// the above license grant, this restriction and the following disclaimer, | ||
// must be included in all copies of the Software, in whole or in part, and | ||
// all derivative works of the Software, unless such copies or derivative | ||
// works are solely in the form of machine-executable object code generated by | ||
// a source language processor. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT | ||
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE | ||
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, | ||
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
// DEALINGS IN THE SOFTWARE. | ||
|
||
#ifndef OPENMC_BISECT_H | ||
#define OPENMC_BISECT_H | ||
|
||
#include <iostream> | ||
|
||
template<class F, class Tol> | ||
std::pair<double, double> bisect( | ||
F f, double min, double max, Tol tol, std::uintmax_t& max_iter) | ||
{ | ||
double fmin = f(min); | ||
double fmax = f(max); | ||
if (fmin == 0) { | ||
max_iter = 2; | ||
return std::make_pair(min, min); | ||
} | ||
if (fmax == 0) { | ||
max_iter = 2; | ||
return std::make_pair(max, max); | ||
} | ||
|
||
// | ||
// Error checking: | ||
// | ||
if (min >= max) { | ||
throw std::invalid_argument("Arguments min and max in wrong order"); | ||
} | ||
if (fmin * fmax >= 0) { | ||
throw std::invalid_argument( | ||
"No change of sign, either there is no root to find, or there are " | ||
"multiple roots in the interval"); | ||
} | ||
|
||
// | ||
// Three function invocations so far: | ||
// | ||
std::uintmax_t count = max_iter; | ||
if (count < 3) { | ||
count = 0; | ||
} else { | ||
count -= 3; | ||
} | ||
|
||
while (count && (0 == tol(min, max))) { | ||
double mid = (min + max) / 2; | ||
double fmid = f(mid); | ||
if ((mid == max) || (mid == min)) | ||
break; | ||
if (fmid == 0) { | ||
min = max = mid; | ||
break; | ||
} else if (std::signbit(fmid) != std::signbit(fmin)) { | ||
max = mid; | ||
} else { | ||
min = mid; | ||
fmin = fmid; | ||
} | ||
--count; | ||
} | ||
|
||
max_iter -= count; | ||
|
||
return std::make_pair(min, max); | ||
} | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
#define OPENMC_SURFACE_H | ||
|
||
#include <limits> // For numeric_limits | ||
#include <list> | ||
#include <string> | ||
#include <unordered_map> | ||
|
||
|
@@ -16,6 +17,9 @@ | |
#include "openmc/position.h" | ||
#include "openmc/vector.h" | ||
|
||
#include "tpms.h" | ||
#include "tpms_functions.h" | ||
|
||
namespace openmc { | ||
|
||
//============================================================================== | ||
|
@@ -53,6 +57,10 @@ class Surface { | |
//! false otherwise. | ||
bool sense(Position r, Direction u) const; | ||
|
||
//! Determine if the surface has a special type and needs specific ray-tracing treatment. | ||
//! \return the name of the special treatment. | ||
virtual bool is_tpms() const { return false; }; | ||
|
||
//! Determine the direction of a ray reflected from the surface. | ||
//! \param[in] r The point at which the ray is incident. | ||
//! \param[in] u Incident direction of the ray | ||
|
@@ -76,7 +84,15 @@ class Surface { | |
//! \param u The direction of the ray. | ||
//! \param coincident A hint to the code that the given point should lie | ||
//! exactly on the surface. | ||
virtual double distance(Position r, Direction u, bool coincident) const = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably leave this as =0. That prevents programmer mistakes and ensures that base classes implement this method, which will surely be unique for each surface type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had to edit that part because of the special treatment of tpms : cell.cpp l833
I am open to another solution but I always failed to create an object surface which is specifically is a |
||
virtual double distance(Position r, Direction u, bool coincident) const | ||
{ | ||
return 0.; | ||
}; | ||
virtual double distance( | ||
Position r, Direction u, bool coincident, double max_range) const | ||
{ | ||
return 0.; | ||
}; | ||
|
||
//! Compute the local outward normal direction of the surface. | ||
//! \param r A 3D Cartesian coordinate. | ||
|
@@ -327,6 +343,67 @@ class SurfaceQuadric : public CSGSurface { | |
double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_; | ||
}; | ||
|
||
//============================================================================== | ||
//! Triply Periodic Minimal Surfaces (TPMS) | ||
// | ||
//! Surfaces defined by an implicit function f(x,y,z) = c with c, an isovalue. | ||
//! Various equations exists : | ||
//! * Schwarz-P : cos(l*x) + cos(l*y) + cos(l*z) = c | ||
//! * Gyroid : sin(l*x)cos(l*z) + sin(l*y)cos(l*x) + sin(l*z)cos(l*y) = c | ||
//! * Diamond : sin(l*x)sin(l*y)sin(l*z) + sin(l*x)cos(l*y)cos(l*z) + | ||
//! cos(l*x)sin(l*y)cos(l*z) + cos(l*x)cos(l*y)sin(l*z) = c | ||
//! with l=2pi/L, L is defined as the pitch of the TPMS lattice. | ||
//! L and c are constants for this class. | ||
//============================================================================== | ||
pferney05 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
class SurfaceTPMS : public CSGSurface { | ||
public: | ||
explicit SurfaceTPMS(pugi::xml_node surf_node); | ||
bool is_tpms() const { return true; }; | ||
double evaluate(Position r) const; | ||
double distance( | ||
Position r, Direction u, bool coincident, double max_range) const; | ||
Direction normal(Position r) const; | ||
void to_hdf5_inner(hid_t group_id) const; | ||
|
||
double isovalue, pitch; | ||
double x0, y0, z0; | ||
double a, b, c, d, e, f, g, h, i; | ||
std::string surface_type; | ||
|
||
private: | ||
std::array<std::string, 3> tpms_types = {"Schwarz_P", "Gyroid", "Diamond"}; | ||
}; | ||
|
||
//============================================================================== | ||
//! Interpolated Triply Periodic Minimal Surfaces (TPMS) | ||
// | ||
//! Same surfaces as above but with isovalue c and pitch L defined as a function | ||
//! of x,y and z. | ||
//============================================================================== | ||
|
||
class SurfaceFunctionTPMS : public CSGSurface { | ||
public: | ||
explicit SurfaceFunctionTPMS(pugi::xml_node surf_node); | ||
~SurfaceFunctionTPMS(); | ||
bool is_tpms() const { return true; }; | ||
double evaluate(Position r) const; | ||
double distance( | ||
Position r, Direction u, bool coincident, double max_range) const; | ||
Direction normal(Position r) const; | ||
void to_hdf5_inner(hid_t group_id) const; | ||
|
||
unique_ptr<FunctionForTPMS> fPitch; | ||
unique_ptr<FunctionForTPMS> fIsovalue; | ||
double x0, y0, z0; | ||
double a, b, c, d, e, f, g, h, i; | ||
std::string surface_type; | ||
std::string function_type; | ||
|
||
private: | ||
std::array<std::string, 3> tpms_types = {"Schwarz_P", "Gyroid", "Diamond"}; | ||
}; | ||
|
||
//============================================================================== | ||
//! A toroidal surface described by the quartic torus lies in the x direction | ||
// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, the base class shouldn't have to have any methods like this. I believe this is not in line with the "dependency inversion principle" which, if followed, means you'll have cleaner object oriented code. do you know of any other way to accomplish this without letting the base class know we're dealing with a TPMS surface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I tried to do a dynamic casting first, like the following:
But it felt even more dirty, and it might hurt cases where there are a lot of surfaces. Moreover, we can probably generalize that feature to allow ray-tracing optimization to other surfaces.
Let me know which solution would be best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yeah, you shouldn't need to use a dynamic case. ideally, you would just put virtual methods on the surface class that make it behave correctly. dynamic casting means you have done your abstractions poorly. do you have any ideas about what the general logic at hand here might be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically, I do the following in the cell.cpp: