@@ -9,6 +9,7 @@ FlowCell::FlowCell(
99 int N_terms,
1010 int n_int,
1111 const std::string& event_scheme,
12+ const std::string& transverse_sampling_scheme,
1213 bool perfectly_aligned
1314)
1415 : width(width),
@@ -20,34 +21,72 @@ FlowCell::FlowCell(
2021 N_terms(N_terms),
2122 n_int(n_int),
2223 event_scheme(event_scheme),
24+ transverse_sampling_scheme(transverse_sampling_scheme),
2325 perfectly_aligned(perfectly_aligned)
2426{
27+ this ->validate_physical_parameters ();
2528 this ->validate_event_scheme ();
29+ this ->validate_transverse_sampling_scheme ();
2630 this ->initialize ();
2731}
2832
2933std::tuple<std::vector<double >, std::vector<double >, std::vector<double >>
3034FlowCell::sample_transverse_profile (int n_samples) const {
35+ if (n_samples < 0 ) {
36+ throw std::runtime_error (" n_samples must be non negative." );
37+ }
38+
3139 std::vector<double > y_samples;
3240 std::vector<double > z_samples;
3341 std::vector<double > velocity_samples;
3442
35- y_samples.reserve (n_samples);
36- z_samples.reserve (n_samples);
37- velocity_samples.reserve (n_samples);
43+ y_samples.reserve (static_cast <std:: size_t >( n_samples) );
44+ z_samples.reserve (static_cast <std:: size_t >( n_samples) );
45+ velocity_samples.reserve (static_cast <std:: size_t >( n_samples) );
3846
3947 std::random_device random_device;
4048 std::mt19937 generator (random_device ());
49+
4150 std::uniform_real_distribution<double > y_distribution (-sample.width / 2.0 , sample.width / 2.0 );
4251 std::uniform_real_distribution<double > z_distribution (-sample.height / 2.0 , sample.height / 2.0 );
52+ std::uniform_real_distribution<double > acceptance_distribution (0.0 , 1.0 );
4353
4454 for (int sample_index = 0 ; sample_index < n_samples; ++sample_index) {
45- const double y = perfectly_aligned ? 0.0 : y_distribution (generator);
46- const double z = perfectly_aligned ? 0.0 : z_distribution (generator);
55+ double y = 0.0 ;
56+ double z = 0.0 ;
57+ double velocity = u_center;
58+
59+ if (!perfectly_aligned) {
60+ bool accepted_sample = false ;
61+
62+ while (!accepted_sample) {
63+ y = y_distribution (generator);
64+ z = z_distribution (generator);
65+ velocity = this ->get_velocity (y, z, dpdx);
66+
67+ if (transverse_sampling_scheme == " uniform-random" ) {
68+ accepted_sample = true ;
69+ } else if (transverse_sampling_scheme == " velocity-weighted" ) {
70+ const double acceptance_probability = velocity / u_center;
71+
72+ if (
73+ acceptance_probability >= 1.0 ||
74+ acceptance_distribution (generator) <= acceptance_probability
75+ ) {
76+ accepted_sample = true ;
77+ }
78+ } else {
79+ throw std::runtime_error (
80+ " Unsupported transverse_sampling_scheme '" + transverse_sampling_scheme +
81+ " '. Expected one of: 'velocity-weighted', 'uniform-random'."
82+ );
83+ }
84+ }
85+ }
4786
4887 y_samples.push_back (y);
4988 z_samples.push_back (z);
50- velocity_samples.push_back (this -> get_velocity (y, z, dpdx) );
89+ velocity_samples.push_back (velocity );
5190 }
5291
5392 return std::make_tuple (y_samples, z_samples, velocity_samples);
@@ -70,20 +109,33 @@ void FlowCell::initialize() {
70109 throw std::runtime_error (" Computed center velocity is non positive." );
71110 }
72111
73- const double sample_area = sample_volume_flow / u_center;
74- const double sample_height = std::sqrt (sample_area * height / width);
75- const double sample_width = (width / height) * sample_height;
76- const double average_flow_speed_sample = sample_volume_flow / sample_area;
112+ const double sample_core_scale = this ->compute_sample_core_scale ();
113+ const double sample_width = sample_core_scale * width;
114+ const double sample_height = sample_core_scale * height;
115+ const double sample_area = sample_width * sample_height;
116+ const double sample_average_flow_speed = sample_volume_flow / sample_area;
117+ const double sample_max_flow_speed = this ->get_velocity (0.0 , 0.0 , dpdx);
77118
78119 this ->sample = FluidRegion (
79120 sample_height,
80121 sample_width,
81122 sample_volume_flow,
82- u_center ,
83- average_flow_speed_sample
123+ sample_max_flow_speed ,
124+ sample_average_flow_speed
84125 );
85126
86- this ->sheath = FluidRegion (height, width, sheath_volume_flow);
127+ const double sheath_area = area - sample_area;
128+ const double sheath_average_flow_speed = sheath_area > 0.0
129+ ? sheath_volume_flow / sheath_area
130+ : 0.0 ;
131+
132+ this ->sheath = FluidRegion (
133+ height,
134+ width,
135+ sheath_volume_flow,
136+ u_center,
137+ sheath_average_flow_speed
138+ );
87139}
88140
89141void FlowCell::validate_event_scheme () const {
@@ -99,6 +151,99 @@ void FlowCell::validate_event_scheme() const {
99151 }
100152}
101153
154+ void FlowCell::validate_transverse_sampling_scheme () const {
155+ if (
156+ transverse_sampling_scheme != " velocity-weighted" &&
157+ transverse_sampling_scheme != " uniform-random"
158+ ) {
159+ throw std::runtime_error (
160+ " Unsupported transverse_sampling_scheme '" + transverse_sampling_scheme +
161+ " '. Expected one of: 'velocity-weighted', 'uniform-random'."
162+ );
163+ }
164+ }
165+
166+ void FlowCell::validate_physical_parameters () const {
167+ if (width <= 0.0 ) {
168+ throw std::runtime_error (" width must be strictly positive." );
169+ }
170+
171+ if (height <= 0.0 ) {
172+ throw std::runtime_error (" height must be strictly positive." );
173+ }
174+
175+ if (sample_volume_flow < 0.0 ) {
176+ throw std::runtime_error (" sample_volume_flow must be non negative." );
177+ }
178+
179+ if (sheath_volume_flow < 0.0 ) {
180+ throw std::runtime_error (" sheath_volume_flow must be non negative." );
181+ }
182+
183+ if (sample_volume_flow + sheath_volume_flow <= 0.0 ) {
184+ throw std::runtime_error (" The total volume flow must be strictly positive." );
185+ }
186+
187+ if (viscosity <= 0.0 ) {
188+ throw std::runtime_error (" viscosity must be strictly positive." );
189+ }
190+
191+ if (N_terms <= 0 ) {
192+ throw std::runtime_error (" N_terms must be strictly positive." );
193+ }
194+
195+ if (n_int < 2 ) {
196+ throw std::runtime_error (" n_int must be greater than or equal to 2." );
197+ }
198+ }
199+
200+ double FlowCell::compute_sample_core_scale () const {
201+ if (sample_volume_flow == 0.0 ) {
202+ return 0.0 ;
203+ }
204+
205+ if (sheath_volume_flow == 0.0 ) {
206+ return 1.0 ;
207+ }
208+
209+ const double full_channel_flow = this ->compute_region_flow (width, height, dpdx);
210+
211+ if (full_channel_flow <= 0.0 ) {
212+ throw std::runtime_error (" Computed full channel flow is non positive." );
213+ }
214+
215+ if (sample_volume_flow > full_channel_flow) {
216+ throw std::runtime_error (" sample_volume_flow exceeds the computed full channel flow." );
217+ }
218+
219+ double lower_scale = 0.0 ;
220+ double upper_scale = 1.0 ;
221+
222+ constexpr int maximum_iterations = 80 ;
223+ constexpr double relative_tolerance = 1e-10 ;
224+
225+ for (int iteration = 0 ; iteration < maximum_iterations; ++iteration) {
226+ const double middle_scale = 0.5 * (lower_scale + upper_scale);
227+ const double region_width = middle_scale * width;
228+ const double region_height = middle_scale * height;
229+ const double region_flow = this ->compute_region_flow (region_width, region_height, dpdx);
230+
231+ const double relative_error = std::abs (region_flow - sample_volume_flow) / sample_volume_flow;
232+
233+ if (relative_error < relative_tolerance) {
234+ return middle_scale;
235+ }
236+
237+ if (region_flow < sample_volume_flow) {
238+ lower_scale = middle_scale;
239+ } else {
240+ upper_scale = middle_scale;
241+ }
242+ }
243+
244+ return 0.5 * (lower_scale + upper_scale);
245+ }
246+
102247double FlowCell::get_velocity (double y, double z, double dpdx_local) const {
103248 double velocity = 0.0 ;
104249 const double prefactor =
@@ -117,11 +262,36 @@ double FlowCell::get_velocity(double y, double z, double dpdx_local) const {
117262 return prefactor * velocity;
118263}
119264
120- double FlowCell::compute_channel_flow (double dpdx_input) const {
121- const double y_min = -width / 2.0 ;
122- const double y_max = width / 2.0 ;
123- const double z_min = -height / 2.0 ;
124- const double z_max = height / 2.0 ;
265+ double FlowCell::compute_region_flow (
266+ const double region_width,
267+ const double region_height,
268+ const double dpdx_input
269+ ) const
270+ {
271+ if (region_width < 0.0 ) {
272+ throw std::runtime_error (" region_width must be non negative." );
273+ }
274+
275+ if (region_height < 0.0 ) {
276+ throw std::runtime_error (" region_height must be non negative." );
277+ }
278+
279+ if (region_width > width) {
280+ throw std::runtime_error (" region_width cannot exceed the channel width." );
281+ }
282+
283+ if (region_height > height) {
284+ throw std::runtime_error (" region_height cannot exceed the channel height." );
285+ }
286+
287+ if (region_width == 0.0 || region_height == 0.0 ) {
288+ return 0.0 ;
289+ }
290+
291+ const double y_min = -region_width / 2.0 ;
292+ const double y_max = region_width / 2.0 ;
293+ const double z_min = -region_height / 2.0 ;
294+ const double z_max = region_height / 2.0 ;
125295
126296 const double dy = (y_max - y_min) / static_cast <double >(n_int - 1 );
127297 const double dz = (z_max - z_min) / static_cast <double >(n_int - 1 );
@@ -140,6 +310,10 @@ double FlowCell::compute_channel_flow(double dpdx_input) const {
140310 return sum * dy * dz;
141311}
142312
313+ double FlowCell::compute_channel_flow (double dpdx_input) const {
314+ return this ->compute_region_flow (width, height, dpdx_input);
315+ }
316+
143317std::vector<double >
144318FlowCell::sample_arrival_times_uniform_random (
145319 const std::size_t n_events,
@@ -250,4 +424,4 @@ FlowCell::sample_arrival_times(
250424 " Unsupported event_scheme '" + event_scheme +
251425 " '. Expected one of: 'uniform-random', 'linear', 'poisson'."
252426 );
253- }
427+ }
0 commit comments