51 Point edge1 = p2 - p1;
52 Point edge2 = p3 - p1;
58 double crossProductMagnitude =
mag(crossProduct);
60 return 0.5 * crossProductMagnitude;
73 inline void compute_barycentric_coordinates_coefficients(
const SF::Point& p1,
const SF::Point& p2,
const SF::Point& P, vector<double>& interpolationCoefficients,
double & lengthSquared)
79 double lambda2 =
inner_prod(v, d) / lengthSquared;
80 double lambda1 = 1.0 - lambda2;
82 interpolationCoefficients[0] = lambda1;
83 interpolationCoefficients[1] = lambda2;
97 inline void compute_barycentric_coordinates_coefficients(
const SF::Point& p1,
const SF::Point& p2,
const SF::Point& p3,
const SF::Point& P, vector<double>& interpolationCoefficients,
double & area)
100 area = computeTriangleArea(p1, p2, p3);
115 double denom = d00 * d11 - d01 * d01;
116 double beta = (d11 * d20 - d01 * d21) / denom;
117 double gamma = (d00 * d21 - d01 * d20) / denom;
118 double alpha = 1.0 - beta - gamma;
119 interpolationCoefficients[0] = alpha;
120 interpolationCoefficients[1] = beta;
121 interpolationCoefficients[2] = gamma;
140 double area1 = computeTriangleArea(p1, p2, p4);
141 double area2 = computeTriangleArea(p2, p3, p4);
142 area = area1 + area2;
159 interpolationCoefficients[0] = (1 - u) * (1 - v);
160 interpolationCoefficients[1] = u * (1 - v);
161 interpolationCoefficients[2] = u * v;
162 interpolationCoefficients[3] = (1 - u) * v;
176 inline void compute_integrate_matrix_barycentric(vector<SF::Point> face_coordinates,
SF_int nnodes, dmat<SF_real> & ebuff, dmat<SF_real> & ebuff_s, dmat<SF_real> & ebuff_counter, S mass_scale)
182 for (
int i = 0; i < nnodes; ++i)
184 x += face_coordinates[i].x;
185 y += face_coordinates[i].y;
186 z += face_coordinates[i].z;
188 SF::Point b; b.
x = x/nnodes; b.
y = y/nnodes; b.
z = z/nnodes;
190 vector<double> interpolationCoefficients(nnodes);
191 double area, weighted_area;
193 compute_barycentric_coordinates_coefficients(face_coordinates[0], face_coordinates[1], b, interpolationCoefficients, area);
197 compute_barycentric_coordinates_coefficients(face_coordinates[0], face_coordinates[1], face_coordinates[2], b, interpolationCoefficients, area);
200 compute_barycentric_coordinates_coefficients(face_coordinates[0], face_coordinates[1], face_coordinates[2], face_coordinates[3], b, interpolationCoefficients, area);
203 weighted_area = area/nnodes;
206 ebuff[0][0] = interpolationCoefficients[0];
207 ebuff[0][1] = interpolationCoefficients[1];
209 ebuff_counter[0][0] = interpolationCoefficients[0];
210 ebuff_counter[0][1] = interpolationCoefficients[1];
212 ebuff_s[0][0] = mass_scale*weighted_area;
213 ebuff_s[0][1] = mass_scale*weighted_area;
216 ebuff[0][0] = interpolationCoefficients[0];
217 ebuff[0][1] = interpolationCoefficients[1];
218 ebuff[0][2] = interpolationCoefficients[2];
220 ebuff_counter[0][0] = interpolationCoefficients[0];
221 ebuff_counter[0][1] = interpolationCoefficients[1];
222 ebuff_counter[0][2] = interpolationCoefficients[2];
224 ebuff_s[0][0] = mass_scale*weighted_area;
225 ebuff_s[0][1] = mass_scale*weighted_area;
226 ebuff_s[0][2] = mass_scale*weighted_area;
229 ebuff[0][0] = interpolationCoefficients[0];
230 ebuff[0][1] = interpolationCoefficients[1];
231 ebuff[0][2] = interpolationCoefficients[2];
232 ebuff[0][3] = interpolationCoefficients[3];
234 ebuff_counter[0][0] = interpolationCoefficients[0];
235 ebuff_counter[0][1] = interpolationCoefficients[1];
236 ebuff_counter[0][2] = interpolationCoefficients[2];
237 ebuff_counter[0][3] = interpolationCoefficients[3];
239 ebuff_s[0][0] = mass_scale*weighted_area;
240 ebuff_s[0][1] = mass_scale*weighted_area;
241 ebuff_s[0][2] = mass_scale*weighted_area;
242 ebuff_s[0][3] = mass_scale*weighted_area;
291 template<
class tuple_key,
class tuple_value,
class tri_key,
class tri_value,
class quad_key,
class quad_value,
class T,
class S>
292 inline void assemble_restrict_operator( abstract_matrix<T,S> & B,
293 abstract_matrix<T,S> & Bi,
294 abstract_matrix<T,S> & BsM,
297 std::pair<mesh_int_t,mesh_int_t>> map_vertex_tag_to_dof_petsc,
299 std::pair<tuple_value, tuple_value>> line_face,
301 std::pair<tri_value, tri_value>> tri_face,
303 std::pair<quad_value, quad_value>> quad_face,
304 const meshdata<mesh_int_t,mesh_real_t> & surface_mesh,
305 const meshdata<mesh_int_t,mesh_real_t> & emi_mesh,
309 T row_dpn = 1; T col_dpn = 1;
311 const vector<SF_int> & petsc_nbr = emi_mesh.get_numbering(
NBR_PETSC);
315 for(
size_t i=0; i<rnod_emi.
size(); i++){
316 g2l_emi[rnod_emi[i]] = i;
317 l2g_emi[i] = rnod_emi[i];
325 for(
size_t i=0; i<rnod.
size(); i++){
340 const T* con_emi = emi_mesh.con.data();
341 const T* con = surface_mesh.con.data();
344 for(
size_t eidx=0; eidx < surface_mesh.l_numelem; eidx++)
346 T tag = surface_mesh.tag[eidx];
349 std::vector<int> elem_nodes;
350 std::vector<int> elem_nodes_old;
351 std::vector<int> petsc_first;
352 std::vector<int> petsc_second;
365 for (
int n = surface_mesh.dsp[eidx]; n < surface_mesh.dsp[eidx+1];n++)
367 T l_idx = surface_mesh.con[n];
369 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
370 Index_tag_old = std::make_pair(l2g[l_idx],tag);
371 mesh_int_t Index_new = map_vertex_tag_to_dof_petsc[Index_tag_old].first;
372 elem_nodes.push_back(Index_new);
373 elem_nodes_old.push_back(l2g[l_idx]);
376 std::sort(elem_nodes.begin(),elem_nodes.end());
377 if(elem_nodes.size()==2){
380 key.v1 = elem_nodes[0];
381 key.v2 = elem_nodes[1];
382 std::pair<tuple_value, tuple_value> value = line_face[key];
384 tag_first = value.first.tag;
385 tag_second = value.second.tag;
387 rank_first = value.first.rank;
388 rank_second = value.second.rank;
390 mem_first = value.first.mem;
391 mem_second = value.second.mem;
393 else if(elem_nodes.size()==3){
395 key.v1 = elem_nodes[0];
396 key.v2 = elem_nodes[1];
397 key.v3 = elem_nodes[2];
398 std::pair<tri_value, tri_value> value = tri_face[key];
400 tag_first = value.first.tag;
401 tag_second = value.second.tag;
403 rank_first = value.first.rank;
404 rank_second = value.second.rank;
406 mem_first = value.first.mem;
407 mem_second = value.second.mem;
409 else if(elem_nodes.size()==4){
411 key.v1 = elem_nodes[0];
412 key.v2 = elem_nodes[1];
413 key.v3 = elem_nodes[2];
414 key.v4 = elem_nodes[3];
415 std::pair<quad_value, quad_value> value = quad_face[key];
417 tag_first = value.first.tag;
418 tag_second = value.second.tag;
420 rank_first = value.first.rank;
421 rank_second = value.second.rank;
423 mem_first = value.first.mem;
424 mem_second = value.second.mem;
427 if(tag != tag_first) std::swap(tag_first, tag_second);
429 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
431 for (
int indx = 0; indx < elem_nodes_old.size(); ++indx)
433 std::pair <mesh_int_t,mesh_int_t> Index_tag_old_first;
434 Index_tag_old_first = std::make_pair(elem_nodes_old[indx],tag_first);
436 auto it_first = map_vertex_tag_to_dof_petsc.find(Index_tag_old_first);
437 if (it_first == map_vertex_tag_to_dof_petsc.end()) {
438 std::cerr <<
"ERROR: tag_first=" << tag_first <<
" not found for vertex=" << elem_nodes_old[indx] << std::endl;
439 throw std::runtime_error(
"PETSc index not found for first tag");
441 std::pair <mesh_int_t,mesh_int_t> newIndex_petsc_first = it_first->second;
442 mesh_int_t newIndex_first = newIndex_petsc_first.first;
443 petsc_first.push_back(newIndex_petsc_first.second);
445 std::pair <mesh_int_t,mesh_int_t> Index_tag_old_second;
446 Index_tag_old_second = std::make_pair(elem_nodes_old[indx],tag_second);
448 auto it_second = map_vertex_tag_to_dof_petsc.find(Index_tag_old_second);
449 if (it_second == map_vertex_tag_to_dof_petsc.end()) {
450 std::cerr <<
"ERROR: tag_second=" << tag_second <<
" not found for vertex=" << elem_nodes_old[indx] << std::endl;
451 throw std::runtime_error(
"PETSc index not found for counter-face tag");
453 std::pair <mesh_int_t,mesh_int_t> newIndex_petsc_second = it_second->second;
454 mesh_int_t newIndex_second = newIndex_petsc_second.first;
455 petsc_second.push_back(newIndex_petsc_second.second);
458 if(mem_first==1 and elemTag_surface_mesh[eidx]==1)
460 else if (mem_first==2 and tag<tag_second)
464 SF_int nnodes = elem_nodes.size();
465 vector<SF::Point> face_coordinates(nnodes);
467 row_idx.resize(nnodes*row_dpn);
468 row_idx_counter.resize(nnodes*row_dpn);
469 col_idx.resize(nnodes*col_dpn);
470 col_idx_counter.resize(nnodes*col_dpn);
473 for(
SF_int i=0; i<nnodes; i++){
475 for(
short j=0; j<row_dpn; j++){
476 row_idx.data()[i*row_dpn + j] = emi_surfmesh_elem[eidx]*row_dpn;
479 for(
short j=0; j<col_dpn; j++){
480 col_idx[i*col_dpn + j] = (petsc_first[i])*col_dpn + j;
481 col_idx_counter[i*col_dpn + j] = (petsc_second[i])*col_dpn + j;
487 ebuff.
assign(nnodes, nnodes, 0.0);
488 ebuff_s.assign(nnodes, nnodes, 0.0);
490 ebuff_counter.assign(nnodes, nnodes, 0.0);
492 for (
int n = surface_mesh.dsp[eidx], i = 0; n < surface_mesh.dsp[eidx+1];n++,i++)
494 T l_idx = surface_mesh.con[n];
496 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
497 Index_tag_old = std::make_pair(l2g[l_idx],tag);
498 mesh_int_t Index_new = map_vertex_tag_to_dof_petsc[Index_tag_old].first;
500 double x = emi_mesh.xyz[g2l_emi[Index_new]*3+0];
501 double y = emi_mesh.xyz[g2l_emi[Index_new]*3+1];
502 double z = emi_mesh.xyz[g2l_emi[Index_new]*3+2];
504 face_coordinates[i].x = x;
505 face_coordinates[i].y = y;
506 face_coordinates[i].z = z;
508 compute_integrate_matrix_barycentric(face_coordinates, nnodes, ebuff, ebuff_s, ebuff_counter, mass_scale);
511 const bool add =
true;
514 Bi.set_values(row_idx, col_idx, ebuff.data(), add);
515 Bi.set_values(row_idx, col_idx_counter, ebuff_counter.data(), add);
518 B.set_values(row_idx, col_idx, ebuff.data(), add);
519 ebuff_counter*=(-
sign);
520 B.set_values(row_idx, col_idx_counter, ebuff_counter.data(), add);
524 BsM.set_values(col_idx, row_idx, ebuff_s.data(), add);
528 Bi.finish_assembly();
529 BsM.finish_assembly();
548 template<
class tuple_key,
class tuple_value,
class tri_key,
class tri_value,
class quad_key,
class quad_value,
class T,
class S>
549 inline void assemble_lhs_emi(abstract_matrix<T,S> & mat,
550 abstract_matrix<T,S> & mat_surf,
551 const meshdata<mesh_int_t,mesh_real_t> & emi_mesh,
552 const meshdata<mesh_int_t,mesh_real_t> & surface_mesh,
553 hashmap::unordered_map<std::pair<mesh_int_t,mesh_int_t>, std::pair<mesh_int_t,mesh_int_t>> map_vertex_tag_to_dof_petsc,
555 std::pair<tuple_value, tuple_value>> line_face,
557 std::pair<tri_value, tri_value>> tri_face,
559 std::pair<quad_value, quad_value>> quad_face,
560 matrix_integrator<mesh_int_t,mesh_real_t> & stiffness_integrator,
561 matrix_integrator<mesh_int_t, mesh_real_t> & mass_integrator,
566 T row_dpn = 1; T col_dpn = 1;
576 const vector<mesh_int_t> & stiffness_petsc_nbr = emi_mesh.get_numbering(
NBR_PETSC);
577 element_view<mesh_int_t, mesh_real_t> stiffness_view(emi_mesh,
NBR_PETSC);
580 for(
size_t eidx=0; eidx < emi_mesh.l_numelem; eidx++)
583 stiffness_view.set_elem(eidx);
586 mesh_int_t nnodes = stiffness_view.num_nodes();
588 row_idx.resize(nnodes*row_dpn);
589 col_idx.resize(nnodes*col_dpn);
590 canonic_indices<mesh_int_t,SF_int>(stiffness_view.nodes(), stiffness_petsc_nbr.data(), nnodes, row_dpn, row_idx.data());
591 canonic_indices<mesh_int_t,SF_int>(stiffness_view.nodes(), stiffness_petsc_nbr.data(), nnodes, col_dpn, col_idx.data());
594 stiffness_integrator(stiffness_view, ebuff);
595 ebuff *= stiffness_scale;
598 const bool add =
true;
599 mat.set_values(row_idx, col_idx, ebuff.data(), add);
606 const vector<SF_int> & ref_nbr = surface_mesh.get_numbering(
SF::NBR_REF);
607 const vector<SF_int> & petsc_nbr = surface_mesh.get_numbering(
SF::NBR_PETSC);
612 for(
size_t i=0; i<rnod_emi.
size(); i++){
613 g2l_emi[rnod_emi[i]] = i;
614 l2g_emi[i] = rnod_emi[i];
620 for(
size_t i=0; i<rnod.
size(); i++){
626 element_view<mesh_int_t, mesh_real_t> view(surface_mesh,
NBR_PETSC);
627 for(
size_t eidx=0; eidx < surface_mesh.l_numelem; eidx++)
634 std::vector<int> elem_nodes;
635 std::vector<int> elem_nodes_old;
636 std::vector<int> elem_nodes_first;
637 std::vector<int> elem_nodes_second;
639 std::vector<int> petsc_first;
640 std::vector<int> petsc_second;
645 for (
int n = surface_mesh.dsp[eidx]; n < surface_mesh.dsp[eidx+1];n++)
647 T l_idx = surface_mesh.con[n];
649 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
650 Index_tag_old = std::make_pair(l2g[l_idx],tag);
651 mesh_int_t Index_new = map_vertex_tag_to_dof_petsc[Index_tag_old].first;
652 elem_nodes.push_back(Index_new);
653 elem_nodes_old.push_back(l2g[l_idx]);
656 std::sort(elem_nodes.begin(),elem_nodes.end());
657 if(elem_nodes.size()==2){
660 key.v1 = elem_nodes[0];
661 key.v2 = elem_nodes[1];
662 std::pair<tuple_value, tuple_value> value = line_face[key];
664 tag_first = value.first.tag;
665 tag_second = value.second.tag;
667 else if(elem_nodes.size()==3){
669 key.v1 = elem_nodes[0];
670 key.v2 = elem_nodes[1];
671 key.v3 = elem_nodes[2];
672 std::pair<tri_value, tri_value> value = tri_face[key];
674 tag_first = value.first.tag;
675 tag_second = value.second.tag;
677 else if(elem_nodes.size()==4){
679 key.v1 = elem_nodes[0];
680 key.v2 = elem_nodes[1];
681 key.v3 = elem_nodes[2];
682 key.v4 = elem_nodes[3];
683 std::pair<quad_value, quad_value> value = quad_face[key];
685 tag_first = value.first.tag;
686 tag_second = value.second.tag;
689 if(tag != tag_first) std::swap(tag_first, tag_second);
691 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
693 for (
int indx = 0; indx < elem_nodes_old.size(); ++indx)
695 std::pair <mesh_int_t,mesh_int_t> Index_tag_old_first;
696 Index_tag_old_first = std::make_pair(elem_nodes_old[indx],tag_first);
697 std::pair <mesh_int_t,mesh_int_t> newIndex_petsc_first = map_vertex_tag_to_dof_petsc[Index_tag_old_first];
698 mesh_int_t newIndex_first = newIndex_petsc_first.first;
699 petsc_first.push_back(newIndex_petsc_first.second);
701 std::pair <mesh_int_t,mesh_int_t> Index_tag_old_second;
702 Index_tag_old_second = std::make_pair(elem_nodes_old[indx],tag_second);
703 std::pair <mesh_int_t,mesh_int_t> newIndex_petsc_second = map_vertex_tag_to_dof_petsc[Index_tag_old_second];
704 mesh_int_t newIndex_second = newIndex_petsc_second.first;
705 petsc_second.push_back(newIndex_petsc_second.second);
709 SF_int nnodes = view.num_nodes();
710 idx.resize(nnodes*row_dpn);
711 idx_counter.resize(nnodes*col_dpn);
714 for(T i=0; i<nnodes; i++)
716 for(
short j=0; j<row_dpn; j++){
717 idx[i*row_dpn + j] = (petsc_first[i])*row_dpn + j;
719 for(
short j=0; j<col_dpn; j++){
720 idx_counter[i*col_dpn + j] = (petsc_second[i])*col_dpn + j;
726 const bool add =
true;
729 mass_integrator(view, ebuff);
730 ebuff_mass.assign(nnodes, nnodes, 0.0);
734 ebuff*=0.5 * mass_scale;
737 mat.set_values(idx, idx, ebuff.data(), add);
738 mat.set_values(idx_counter, idx_counter, ebuff.data(), add);
740 mat_surf.set_values(idx, idx, ebuff_mass.data(), add);
741 mat_surf.set_values(idx_counter, idx_counter, ebuff_mass.data(), add);
746 mat.set_values(idx, idx_counter, ebuff.data(), add);
747 mat.set_values(idx_counter, idx, ebuff.data(), add);
749 mat_surf.set_values(idx, idx_counter, ebuff_mass.data(), add);
750 mat_surf.set_values(idx_counter, idx, ebuff_mass.data(), add);
762 mat.finish_assembly();
763 mat_surf.finish_assembly();
783 template<
class tuple_key,
class tuple_value,
class tri_key,
class tri_value,
class quad_key,
class quad_value,
class T,
class S>
784 inline void assign_resting_potential_from_ionic_models_on_myocyte(abstract_matrix<T,S> & Vm_emi,
785 abstract_vector<T, S>* vb,
788 std::pair<T,T>> map_vertex_tag_to_dof_petsc,
790 std::pair<tuple_value, tuple_value>> line_face,
792 std::pair<tri_value, tri_value>> tri_face,
794 std::pair<quad_value, quad_value>> quad_face,
795 const meshdata<T,mesh_real_t> & surface_mesh,
796 const meshdata<T,mesh_real_t> & emi_mesh)
799 T row_dpn = 1; T col_dpn = 1;
801 const vector<SF_int> & petsc_nbr = emi_mesh.get_numbering(
NBR_PETSC);
805 for(
size_t i=0; i<rnod_emi.
size(); i++){
806 g2l_emi[rnod_emi[i]] = i;
807 l2g_emi[i] = rnod_emi[i];
811 const vector<SF_int> & petsc_nbr_surface = surface_mesh.get_numbering(
NBR_PETSC);
818 for(
size_t i=0; i<rnod.
size(); i++){
822 auto vb_data = vb->const_ptr();
825 for(
size_t eidx=0; eidx < surface_mesh.l_numelem; eidx++)
827 T tag = surface_mesh.tag[eidx];
830 std::vector<int> elem_nodes;
839 for (
int n = surface_mesh.dsp[eidx], i = 0; n < surface_mesh.dsp[eidx+1];n++,i++)
841 T l_idx = surface_mesh.con[n];
842 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
843 Index_tag_old = std::make_pair(l2g[l_idx],tag);
844 mesh_int_t Index_new = map_vertex_tag_to_dof_petsc[Index_tag_old].first;
845 elem_nodes.push_back(Index_new);
848 std::sort(elem_nodes.begin(),elem_nodes.end());
849 if(elem_nodes.size()==2){
852 key.v1 = elem_nodes[0];
853 key.v2 = elem_nodes[1];
854 std::pair<tuple_value, tuple_value> value = line_face[key];
856 tag_first = value.first.tag;
857 tag_second = value.second.tag;
859 mem_first = value.first.mem;
860 mem_second = value.second.mem;
862 else if(elem_nodes.size()==3){
864 key.v1 = elem_nodes[0];
865 key.v2 = elem_nodes[1];
866 key.v3 = elem_nodes[2];
867 std::pair<tri_value, tri_value> value = tri_face[key];
869 tag_first = value.first.tag;
870 tag_second = value.second.tag;
872 mem_first = value.first.mem;
873 mem_second = value.second.mem;
875 else if(elem_nodes.size()==4){
877 key.v1 = elem_nodes[0];
878 key.v2 = elem_nodes[1];
879 key.v3 = elem_nodes[2];
880 key.v4 = elem_nodes[3];
881 std::pair<quad_value, quad_value> value = quad_face[key];
883 tag_first = value.first.tag;
884 tag_second = value.second.tag;
886 mem_first = value.first.mem;
887 mem_second = value.second.mem;
890 SF_int nnodes = elem_nodes.size();
891 for (
int i = 0; i < nnodes; ++i)
893 if(mem_first==1 || mem_second==1){
894 tag_2_vm[tag_first] = vb_data[eidx];
895 tag_2_vm[tag_second] = vb_data[eidx];
900 vb->const_release_ptr(vb_data);
915 const vector<mesh_int_t> & petsc_nbr = emi_mesh.get_numbering(
NBR_PETSC);
918 element_view<mesh_int_t, mesh_real_t> view(emi_mesh,
NBR_PETSC);
919 for(
size_t eidx=0; eidx < emi_mesh.l_numelem; eidx++)
923 T tag = emi_mesh.tag[eidx];
927 row_idx.resize(nnodes*row_dpn);val_idx.resize(nnodes*row_dpn);
928 col_idx.resize(nnodes*row_dpn);
929 ebuff.assign(nnodes, nnodes, 0.0);
930 canonic_indices<mesh_int_t,SF_int>(view.nodes(), petsc_nbr.data(), nnodes, row_dpn, row_idx.data());
931 canonic_indices<mesh_int_t,SF_int>(view.nodes(), petsc_nbr.data(), nnodes, col_dpn, col_idx.data());
933 for (
int i = 0; i < nnodes; ++i)
937 if(elemTag_emi_mesh[eidx]==2){
938 ebuff[i][i] = tag_2_vm[tag];
941 Vm_emi.set_values(row_idx, col_idx, ebuff.data(),
false);
943 Vm_emi.finish_assembly();
#define SF_MAX_ELEM_NODES
max #nodes defining an element
std::int32_t SF_int
Use the general std::int32_t as int type.
size_t size() const
The current size of the vector.
void assign(InputIterator s, InputIterator e)
Assign a memory range.
double mag(const Point &vect)
vector magnitude
double inner_prod(const Point &a, const Point &b)
Point cross(const Point &a, const Point &b)
cross product
V clamp(const V val, const W start, const W end)
Clamp a value into an interval [start, end].
@ NBR_PETSC
PETSc numbering of nodes.
@ NBR_ELEM_REF
The element numbering of the reference mesh (the one stored on HD).
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).