Skip to content

Commit 7c84e8f

Browse files
update from master
2 parents 9b4401c + baf809e commit 7c84e8f

File tree

101 files changed

+7503
-981
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+7503
-981
lines changed

src/Core/Algorithms/BrainStimulator/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ SET(Algorithms_BrainStimulator_SRCS
3131
SetConductivitiesToTetMeshAlgorithm.cc
3232
GenerateROIStatisticsAlgorithm.cc
3333
SetupRHSforTDCSandTMSAlgorithm.cc
34+
SimulateForwardMagneticFieldAlgorithm.cc
3435
)
3536

3637
SET(Algorithms_BrainStimulator_HEADERS
3738
ElectrodeCoilSetupAlgorithm.h
3839
SetConductivitiesToTetMeshAlgorithm.h
3940
GenerateROIStatisticsAlgorithm.h
4041
SetupRHSforTDCSandTMSAlgorithm.h
42+
SimulateForwardMagneticFieldAlgorithm.h
4143
share.h
4244
)
4345

@@ -51,6 +53,7 @@ TARGET_LINK_LIBRARIES(Algorithms_BrainStimulator
5153
Core_Datatypes_Legacy_Field
5254
Core_Geometry_Primitives #vectors
5355
Core_Basis #field basis
56+
Core_Algorithms_Legacy_Fields
5457
# Core_Datatypes_Legacy_BrainStimulator
5558
Algorithms_Base
5659
${SCI_BOOST_LIBRARY}
Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
/*
2+
For more information, please see: http://software.sci.utah.edu
3+
4+
The MIT License
5+
6+
Copyright (c) 2009 Scientific Computing and Imaging Institute,
7+
University of Utah.
8+
9+
10+
Permission is hereby granted, free of charge, to any person obtaining a
11+
copy of this software and associated documentation files (the "Software"),
12+
to deal in the Software without restriction, including without limitation
13+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
14+
and/or sell copies of the Software, and to permit persons to whom the
15+
Software is furnished to do so, subject to the following conditions:
16+
17+
The above copyright notice and this permission notice shall be included
18+
in all copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26+
DEALINGS IN THE SOFTWARE.
27+
*/
28+
29+
#include <Core/Datatypes/DenseMatrix.h>
30+
#include <Core/Thread/Barrier.h>
31+
#include <Core/Thread/Parallel.h>
32+
#include <Core/Datatypes/Legacy/Field/Mesh.h>
33+
#include <Core/Datatypes/Legacy/Field/VMesh.h>
34+
#include <Core/Datatypes/Legacy/Field/Field.h>
35+
#include <Core/Datatypes/Legacy/Field/VField.h>
36+
#include <Core/Datatypes/Legacy/Field/FieldInformation.h>
37+
#include <Core/GeometryPrimitives/Point.h>
38+
#include <Core/GeometryPrimitives/Tensor.h>
39+
#include <Core/Algorithms/Base/AlgorithmPreconditions.h>
40+
#include <Core/Algorithms/Base/AlgorithmVariableNames.h>
41+
#include <Core/Logging/ScopedTimeRemarker.h>
42+
#include <Core/Logging/Log.h>
43+
#include <Core/Algorithms/BrainStimulator/SimulateForwardMagneticFieldAlgorithm.h>
44+
#include <string>
45+
#include <vector>
46+
#include <algorithm>
47+
48+
using namespace SCIRun;
49+
using namespace SCIRun::Core::Geometry;
50+
using namespace SCIRun::Core::Datatypes;
51+
using namespace SCIRun::Core::Thread;
52+
using namespace SCIRun::Core::Algorithms;
53+
using namespace SCIRun::Core::Algorithms::BrainStimulator;
54+
using namespace SCIRun::Core::Logging;
55+
56+
AlgorithmInputName SimulateForwardMagneticFieldAlgo::ElectricField("ElectricField");
57+
AlgorithmInputName SimulateForwardMagneticFieldAlgo::ConductivityTensor("ConductivityTensor");
58+
AlgorithmInputName SimulateForwardMagneticFieldAlgo::DipoleSources("DipoleSources");
59+
AlgorithmInputName SimulateForwardMagneticFieldAlgo::DetectorLocations("DetectorLocations");
60+
AlgorithmOutputName SimulateForwardMagneticFieldAlgo::MagneticField("MagneticField");
61+
AlgorithmOutputName SimulateForwardMagneticFieldAlgo::MagneticFieldMagnitudes("MagneticFieldMagnitudes");
62+
63+
class CalcFMField
64+
{
65+
public:
66+
67+
CalcFMField(const AlgorithmBase* algo) : algo_(algo),
68+
np_(-1),efld_(0),ctfld_(0),dipfld_(0),detfld_(0),emsh_(0),ctmsh_(0),dipmsh_(0),detmsh_(0),magfld_(0),magmagfld_(0)
69+
{
70+
}
71+
72+
boost::tuple<FieldHandle, FieldHandle> calc_forward_magnetic_field(FieldHandle efield,
73+
FieldHandle ctfield,
74+
FieldHandle dipoles,
75+
FieldHandle detectors);
76+
77+
private:
78+
void interpolate(int proc, Point p);
79+
void set_up_cell_cache();
80+
void calc_parallel(int proc);
81+
82+
const AlgorithmBase* algo_;
83+
int np_;
84+
std::vector<Vector> interp_value_;
85+
std::vector<std::pair<std::string, Tensor> > tens_;
86+
bool have_tensors_;
87+
88+
struct per_cell_cache {
89+
Vector cur_density_;
90+
Point center_;
91+
double volume_;
92+
};
93+
94+
std::vector<per_cell_cache> cell_cache_;
95+
96+
VField* efld_; // Electric Field
97+
VField* ctfld_; // Conductivity Field
98+
VField* dipfld_; // Dipole Field
99+
VField* detfld_; // Detector Field
100+
101+
VMesh* emsh_; // Electric Field
102+
VMesh* ctmsh_; // Conductivity Field
103+
VMesh* dipmsh_; // Dipole Field
104+
VMesh* detmsh_; // Detector Field
105+
106+
VField* magfld_; // Magnetic Field
107+
VField* magmagfld_; // Magnetic Field Magnitudes
108+
};
109+
110+
void CalcFMField::interpolate(int proc, Point p)
111+
{
112+
emsh_->synchronize(Mesh::ELEM_LOCATE_E);
113+
114+
VMesh::Elem::index_type inside_cell = 0;
115+
bool outside = !(emsh_->locate(inside_cell, p));
116+
117+
VMesh::size_type num_elems = emsh_->num_elems();
118+
119+
for (VMesh::Elem::index_type idx; idx<num_elems; idx++)
120+
{
121+
if (outside || idx != inside_cell)
122+
{
123+
per_cell_cache &c = cell_cache_[idx];
124+
Vector radius = p - c.center_;
125+
126+
Vector valueJXR = Cross(c.cur_density_, radius);
127+
double length = radius.length();
128+
129+
interp_value_[proc] += ((valueJXR / (length * length * length)) * c.volume_);
130+
}
131+
}
132+
}
133+
134+
void CalcFMField::set_up_cell_cache()
135+
{
136+
VMesh::size_type num_elems = emsh_->num_elems();
137+
Vector elemField;
138+
cell_cache_.resize(num_elems);
139+
140+
#ifdef SCIRUN4_CODE_TO_BE_ENABLED_LATER
141+
if (have_tensors_)
142+
{
143+
int material = -1;
144+
for (VMesh::Elem::index_type idx=0; idx<num_elems; idx++)
145+
{
146+
per_cell_cache c;
147+
emsh_->get_center(c.center_,idx);
148+
efld_->get_value(elemField,idx);
149+
ctfld_->get_value(material,idx);
150+
151+
c.cur_density_ = tens_[material].second * -1 * elemField;
152+
c.volume_ = emsh_->get_volume(idx);
153+
cell_cache_[idx] = c;
154+
}
155+
} else
156+
#endif
157+
158+
if (ctfld_ && ctfld_->is_tensor())
159+
{
160+
Tensor ten;
161+
for (VMesh::Elem::index_type idx=0; idx<num_elems; idx++)
162+
{
163+
per_cell_cache c;
164+
emsh_->get_center(c.center_,idx);
165+
efld_->get_value(elemField,idx);
166+
ctfld_->get_value(ten,idx);
167+
168+
c.cur_density_ = ten * -1 * elemField;
169+
c.volume_ = emsh_->get_volume(idx);
170+
cell_cache_[idx] = c;
171+
}
172+
}
173+
else
174+
{
175+
double val;
176+
for (VMesh::Elem::index_type idx=0; idx<num_elems; idx++)
177+
{
178+
per_cell_cache c;
179+
emsh_->get_center(c.center_,idx);
180+
efld_->get_value(elemField,idx);
181+
ctfld_->get_value(val,idx);
182+
c.cur_density_ = val * -1 * elemField;
183+
c.volume_ = emsh_->get_volume(idx);
184+
cell_cache_[idx] = c;
185+
}
186+
}
187+
}
188+
189+
void CalcFMField::calc_parallel(int proc)
190+
{
191+
192+
VMesh::size_type num_nodes = detmsh_->num_nodes();
193+
VMesh::size_type nodes_per_thread = num_nodes/np_;
194+
195+
VMesh::Node::index_type start = proc*nodes_per_thread;
196+
VMesh::Node::index_type end = (proc+1)*nodes_per_thread;
197+
if (proc == (np_-1)) end = num_nodes;
198+
199+
Vector mag_field;
200+
Point pt;
201+
Point pt2;
202+
Vector P;
203+
const double one_over_4_pi = 1.0 / (4 * M_PI);
204+
205+
VMesh::size_type num_dipoles = dipmsh_->num_nodes();
206+
207+
int cnt = 0;
208+
for (VMesh::Node::index_type idx = start; idx < end; idx++ )
209+
{
210+
// finish loop iteration.
211+
212+
detmsh_->get_center(pt, idx);
213+
214+
// init the interp val to 0
215+
interp_value_[proc] = Vector(0,0,0);
216+
interpolate(proc, pt);
217+
218+
mag_field = interp_value_[proc];
219+
220+
Vector normal;
221+
detfld_->get_value(normal,idx);
222+
223+
// iterate over the dipoles.
224+
for (VMesh::Node::index_type dip_idx = 0; dip_idx < num_dipoles; dip_idx++)
225+
{
226+
dipmsh_->get_center(pt2, dip_idx);
227+
dipfld_->value(P,dip_idx);
228+
229+
Vector radius = pt - pt2; // detector - source
230+
Vector valuePXR = Cross(P, radius);
231+
double length = radius.length();
232+
233+
mag_field += valuePXR / (length * length * length);
234+
}
235+
236+
mag_field *= one_over_4_pi;
237+
magmagfld_->set_value(Dot(mag_field, normal),idx);
238+
magfld_->set_value(mag_field,idx);
239+
240+
if (proc == 0)
241+
{
242+
cnt++;
243+
if (cnt == 100)
244+
{
245+
cnt = 0;
246+
algo_->update_progress_max(idx,end);
247+
}
248+
}
249+
}
250+
251+
}
252+
253+
boost::tuple<FieldHandle,FieldHandle> CalcFMField::calc_forward_magnetic_field(FieldHandle efield, FieldHandle ctfield, FieldHandle dipoles, FieldHandle detectors)
254+
{
255+
efld_ = efield->vfield();
256+
ctfld_ = ctfield->vfield();
257+
ctmsh_ = ctfield->vmesh();
258+
dipfld_ = dipoles->vfield();
259+
detfld_ = detectors->vfield();
260+
emsh_ = efield->vmesh();
261+
dipmsh_ = dipoles->vmesh();
262+
detmsh_ = detectors->vmesh();
263+
264+
if (!efld_ || !ctfld_ || !ctmsh_ || !dipfld_ || !detfld_ || !emsh_ || !dipmsh_ || !detmsh_)
265+
{
266+
algo_->error("At least one required input field/mesh has a NULL pointer.");
267+
}
268+
269+
have_tensors_=false; /// we dont support a conductivity_table in a FieldHandle (could not be set in SCIRun4 as well)
270+
271+
#ifdef SCIRUN4_CODE_TO_BE_ENABLED_LATER
272+
// this code should be able to handle Field<Tensor> as well
273+
have_tensors_ = ctfld_->get_property("conductivity_table", tens_);
274+
#endif
275+
LOG_DEBUG(" Note: The original SCIRun4 module looked for a field attribute ''conductivity_table'' of the second module input which could only be set outside of SCIRun4. This function is not available in SCIrun5. " << std::endl);
276+
277+
FieldInformation mfi(detectors);
278+
mfi.make_lineardata();
279+
280+
mfi.make_double();
281+
FieldHandle magnetic_field_magnitudes = CreateField(mfi,detectors->mesh());
282+
if (!magnetic_field_magnitudes)
283+
{
284+
algo_->error("Could not allocate field for magnetic field magnitudes");
285+
}
286+
287+
magmagfld_ = magnetic_field_magnitudes->vfield();
288+
magmagfld_->resize_values();
289+
mfi.make_vector();
290+
FieldHandle magnetic_field = CreateField(mfi,detectors->mesh());
291+
if (!magnetic_field)
292+
{
293+
algo_->error("Could not allocate field for magnetic field");
294+
}
295+
296+
magfld_ = magnetic_field->vfield();
297+
magfld_->resize_values();
298+
299+
// Make sure we have more than zero threads
300+
np_ = Parallel::NumCores();
301+
interp_value_.resize(np_,Vector(0.0,0.0,0.0));
302+
303+
// cache per cell calculations that are used over and over again.
304+
set_up_cell_cache();
305+
306+
#ifdef SCIRUN4_CODE_TO_BE_ENABLED_LATER
307+
// do the parallel work.
308+
Thread::parallel(this, &CalcFMField::calc_parallel, np_, mod);
309+
#endif
310+
311+
Parallel::RunTasks([this](int i) { calc_parallel(i); }, np_);
312+
313+
return boost::make_tuple(magnetic_field, magnetic_field_magnitudes);
314+
315+
}
316+
317+
boost::tuple<FieldHandle, FieldHandle> SimulateForwardMagneticFieldAlgo::run(FieldHandle ElectricField, FieldHandle ConductivityTensors, FieldHandle DipoleSources, FieldHandle DetectorLocations) const
318+
{
319+
if (!ElectricField || !DipoleSources || !DetectorLocations || !ConductivityTensors)
320+
{
321+
THROW_ALGORITHM_INPUT_ERROR("At least one required input has a NULL pointer.");
322+
}
323+
324+
if (!ElectricField->vfield()->is_vector())
325+
{
326+
THROW_ALGORITHM_INPUT_ERROR("Must have Vector field as Electric Field input");
327+
}
328+
329+
if (!DipoleSources->vfield()->is_vector())
330+
{
331+
THROW_ALGORITHM_INPUT_ERROR("Must have Vector field as Dipole Sources input");
332+
}
333+
334+
if (!DetectorLocations->vfield()->is_vector())
335+
{
336+
THROW_ALGORITHM_INPUT_ERROR("Must have Vector field as Detector Locations input");
337+
}
338+
339+
CalcFMField algo(this);
340+
FieldHandle MField, MFieldMagnitudes;
341+
342+
boost::tie(MField,MFieldMagnitudes) = algo.calc_forward_magnetic_field(ElectricField, ConductivityTensors, DipoleSources, DetectorLocations);
343+
344+
return boost::make_tuple(MField, MFieldMagnitudes);
345+
}
346+
347+
AlgorithmOutput SimulateForwardMagneticFieldAlgo::run_generic(const AlgorithmInput& input) const
348+
{
349+
AlgorithmOutput output;
350+
351+
auto efield = input.get<Field>(ElectricField);
352+
auto condtensor = input.get<Field>(ConductivityTensor);
353+
auto dipoles = input.get<Field>(DipoleSources);
354+
auto detectors = input.get<Field>(DetectorLocations);
355+
FieldHandle MField, MFieldMagnitudes;
356+
357+
boost::tie(MField,MFieldMagnitudes) = run(efield, condtensor, dipoles, detectors);
358+
359+
output[MagneticField] = MField;
360+
output[MagneticFieldMagnitudes] = MFieldMagnitudes;
361+
362+
return output;
363+
}
364+

0 commit comments

Comments
 (0)