Skip to content

Commit 1ff1955

Browse files
feat(EdgeEffect): manage Spatial Domain in stochastic simulation
1 parent 02e206d commit 1ff1955

19 files changed

Lines changed: 770 additions & 238 deletions

File tree

bindings/python/src/stochastic/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_geode_python_binding(
2828
"sampling/direct/double_sampler.hpp"
2929
"sampling/random_engine.hpp"
3030
"sampling/distributions.hpp"
31+
"spatial/spatial_domain.hpp"
3132
"stochastic.cpp"
3233
DEPENDENCIES
3334
${PROJECT_NAME}::stochastic

bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ namespace geode
5353
pybind11::class_< FractureSimulationRunner,
5454
std::shared_ptr< FractureSimulationRunner > >(
5555
module, "FractureSimulationRunner" )
56-
.def( pybind11::init< const BoundingBox2D& >(),
56+
.def( pybind11::init< const SpatialDomain< 2 >& >(),
5757
pybind11::arg( "box" ) )
5858
.def( "add_fracture_set_descriptor",
5959
&FractureSimulationRunner::add_fracture_set_descriptor,
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2019 - 2025 Geode-solutions
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*
22+
*/
23+
24+
#include <geode/stochastic/spatial/spatial_domain.hpp>
25+
26+
namespace
27+
{
28+
template < geode::index_t dimension >
29+
void declare_spatial_domain( pybind11::module& module )
30+
{
31+
using Domain = geode::SpatialDomain< dimension >;
32+
using BBox = geode::BoundingBox< dimension >;
33+
34+
const auto pyclass_name =
35+
"SpatialDomain" + std::to_string( dimension ) + "D";
36+
37+
pybind11::class_< Domain >( module, pyclass_name.c_str() )
38+
.def( pybind11::init< const BBox&, double >(),
39+
pybind11::arg( "domain" ), pybind11::arg( "buffer_size" ),
40+
R"doc(
41+
Create a spatial domain composed of:
42+
43+
- a core domain (the VOI)
44+
- a buffer zone
45+
- the extended domain (domain + buffer)
46+
47+
Arguments:
48+
domain (BoundingBox): main domain / VOI
49+
buffer_size (float): buffer thickness
50+
)doc" );
51+
}
52+
} // namespace
53+
namespace geode
54+
{
55+
void define_spatial_domain( pybind11::module& module )
56+
{
57+
declare_spatial_domain< 2 >( module );
58+
declare_spatial_domain< 3 >( module );
59+
}
60+
} // namespace geode

bindings/python/src/stochastic/stochastic.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@
3333

3434
#include "sampling/distributions.hpp"
3535
#include "sampling/random_engine.hpp"
36+
#include "spatial/spatial_domain.hpp"
3637

3738
PYBIND11_MODULE( opengeode_stochastic_py_stochastic, module )
3839
{
3940
module.doc() = "OpenGeode-Stochastic Python binding";
4041
pybind11::class_< geode::StochasticLibrary >( module, "StochasticLibrary" )
4142
.def( "initialize", &geode::StochasticLibrary::initialize );
4243

44+
geode::define_spatial_domain( module );
45+
4346
geode::define_distributions( module );
4447
geode::define_random_engine( module );
4548
geode::define_double_sampler( module );

bindings/python/tests/stochastic/test-py-mh-fractures.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def test_fracture_simulator():
4141
box = og.BoundingBox2D()
4242
box.add_point(og.Point2D([0.0, 0.0]))
4343
box.add_point(og.Point2D([100.0, 100.0]))
44+
domain = stochastic.SpatialDomain2D(box,10.)
4445

4546
# --- Object set
4647
setA = stochastic.FractureSetDescription()
@@ -60,7 +61,7 @@ def test_fracture_simulator():
6061
setA.p20 = 0.06
6162
setA.minimal_spacing = 1.0
6263

63-
runner = stochastic.FractureSimulationRunner(box)
64+
runner = stochastic.FractureSimulationRunner(domain)
6465
runner.add_fracture_set_descriptor(setA)
6566
runner.initialize()
6667

@@ -90,7 +91,7 @@ def test_two_fracture_sets_simulator():
9091
box = og.BoundingBox2D()
9192
box.add_point(og.Point2D([0.0, 0.0]))
9293
box.add_point(og.Point2D([100.0, 100.0]))
93-
94+
domain = stochastic.SpatialDomain2D(box,10.)
9495
# --- Object set A
9596
setA = stochastic.FractureSetDescription()
9697
setA.name = "A"
@@ -117,7 +118,7 @@ def test_two_fracture_sets_simulator():
117118
setB.p20 = 0.03
118119
setB.minimal_spacing = 2.0
119120

120-
runner = stochastic.FractureSimulationRunner(box)
121+
runner = stochastic.FractureSimulationRunner(domain)
121122
runner.add_fracture_set_descriptor(setA)
122123
runner.add_fracture_set_descriptor(setB)
123124
runner.initialize()

include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ namespace geode
121121
this->energy_terms_collection_.add_energy_term(
122122
std::make_unique< DensityTerm< OwnerSegment2D > >(
123123
absl::StrCat( set_desc.name, "_density" ),
124-
set_desc.p20, std::vector< uuid >{ set_id } ) ) );
124+
set_desc.p20, std::vector< uuid >{ set_id },
125+
this->domain_ ) ) );
125126
// spacing
126127
if( set_desc.minimal_spacing < GLOBAL_EPSILON )
127128
{
@@ -137,7 +138,7 @@ namespace geode
137138
std::make_unique< PairwiseTerm< OwnerSegment2D > >(
138139
absl::StrCat( set_desc.name, "_min_spacing" ), 0.,
139140
std::vector< uuid >{ set_id },
140-
std::move( interaction ) ) ) );
141+
std::move( interaction ), this->domain_ ) ) );
141142
}
142143

143144
this->mh_sampler_ =

include/geode/stochastic/sampling/mcmc/models/components/density_term.hpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ namespace geode
3535
public:
3636
explicit DensityTerm( std::string_view name,
3737
double lambda,
38-
std::vector< uuid > targeted_set_ids )
38+
std::vector< uuid > targeted_set_ids,
39+
const SpatialDomain< ObjectType::dim >& domain )
3940
: EnergyTerm< ObjectType >(
40-
name, lambda, std::move( targeted_set_ids ) )
41+
name, lambda, std::move( targeted_set_ids ), domain )
4142
{
4243
}
4344

@@ -50,38 +51,52 @@ namespace geode
5051
double delta_log_add( const ObjectSets< ObjectType >& /*state*/,
5152
const ObjectRef< ObjectType >& new_object ) const override
5253
{
53-
if( !this->is_targeted_set( new_object.set_id ) )
54+
if( !this->is_targeted_set( new_object.set_id )
55+
|| !this->is_anchored_in_domain( new_object.object ) )
5456
{
5557
return 0.0;
5658
}
5759
return this->contribution( 1.0 );
5860
}
5961

60-
double delta_log_remove( const ObjectSets< ObjectType >& /*state*/,
62+
double delta_log_remove( const ObjectSets< ObjectType >& state,
6163
const ObjectId& object_id ) const override
6264
{
63-
if( !this->is_targeted_set( object_id.set_id ) )
65+
if( !this->is_targeted_set( object_id.set_id )
66+
|| !this->is_anchored_in_domain(
67+
state.get_object( object_id ) ) )
6468
{
6569
return 0.0;
6670
}
6771
return this->contribution( -1.0 );
6872
}
6973

70-
double delta_log_change( const ObjectSets< ObjectType >& /*state*/,
71-
const ObjectId& /*old_object_id*/,
72-
const ObjectRef< ObjectType >& /*new_object*/ ) const override
74+
double delta_log_change( const ObjectSets< ObjectType >& state,
75+
const ObjectId& old_object_id,
76+
const ObjectRef< ObjectType >& new_object ) const override
7377
{
74-
return 0.0;
78+
auto old_in = this->is_anchored_in_domain(
79+
state.get_object( old_object_id ) );
80+
auto new_in = this->is_anchored_in_domain( new_object.object );
81+
if( old_in == new_in )
82+
{
83+
return 0.0;
84+
}
85+
return this->contribution( new_in ? 1.0 : -1.0 );
7586
}
7687

7788
double statistic( const ObjectSets< ObjectType >& state ) const override
7889
{
79-
index_t count{ 0 };
80-
for( const auto& set_id : this->targeted_set_ids() )
81-
{
82-
count += state.nb_objects_in_set( set_id );
83-
}
84-
return static_cast< double >( count );
90+
double sum = 0.0;
91+
this->for_each_targeted_object(
92+
state, [&]( const ObjectId& obj_id ) {
93+
if( this->is_anchored_in_domain(
94+
state.get_object( obj_id ) ) )
95+
{
96+
sum += 1.0;
97+
}
98+
} );
99+
return sum;
85100
}
86101
};
87102
} // namespace geode

include/geode/stochastic/sampling/mcmc/models/components/energy_term.hpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <geode/stochastic/common.hpp>
3131
#include <geode/stochastic/spatial/object_sets.hpp>
3232

33+
#include <geode/stochastic/spatial/spatial_domain.hpp>
34+
3335
#include <optional>
3436

3537
namespace geode
@@ -88,9 +90,11 @@ namespace geode
8890
public:
8991
explicit EnergyTerm( std::string_view name,
9092
double param,
91-
std::vector< uuid >&& targeted_set_ids )
93+
std::vector< uuid >&& targeted_set_ids,
94+
const SpatialDomain< ObjectType::dim >& domain )
9295
: energy_scale_{ param },
93-
targeted_set_ids_{ std::move( targeted_set_ids ) }
96+
targeted_set_ids_{ std::move( targeted_set_ids ) },
97+
domain_( domain )
9498
{
9599
std::sort( targeted_set_ids_.begin(), targeted_set_ids_.end() );
96100
IdentifierBuilder builder( *this );
@@ -152,6 +156,18 @@ namespace geode
152156
targeted_set_ids_.begin(), targeted_set_ids_.end(), set_id );
153157
}
154158

159+
bool is_anchored_in_domain( const ObjectType& obj ) const
160+
{
161+
return SpatialDomainChecker< ObjectType >::is_anchored_in_domain(
162+
domain_, obj );
163+
}
164+
165+
bool intersects_domain( const ObjectType& obj ) const
166+
{
167+
return SpatialDomainChecker< ObjectType >::intersects_domain(
168+
domain_, obj );
169+
}
170+
155171
template < typename Func >
156172
void for_each_targeted_object(
157173
const ObjectSets< ObjectType >& state, Func&& do_apply ) const
@@ -169,5 +185,6 @@ namespace geode
169185
private:
170186
detail::EnergyScale energy_scale_;
171187
std::vector< uuid > targeted_set_ids_;
188+
const SpatialDomain< ObjectType::dim > domain_;
172189
};
173190
} // namespace geode

0 commit comments

Comments
 (0)