Page MenuHomec4science

template_cadd.cc
No OneTemporary

File Metadata

Created
Thu, Aug 8, 03:29

template_cadd.cc

/**
* @file template_cadd.cc
*
* @author Guillaume Anciaux <guillaume.anciaux@epfl.ch>
* @author Till Junge <till.junge@epfl.ch>
* @author Jaehyun Cho <jaehyun.cho@epfl.ch>
*
* @date Thu Sep 18 14:51:07 2014
*
* @brief Simple CADD-like coupler
*
* @section LICENSE
*
* Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne)
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
* LibMultiScale is free software: you can redistribute it and/or modify it
* under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* LibMultiScale is distributed in the hope that it will be useful, but
* WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with LibMultiScale. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define TIMER
/* -------------------------------------------------------------------------- */
#include "template_cadd.hh"
#include "lib_bridging.hh"
#include "lm_common.hh"
#include <algorithm>
#include <assert.h>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <unistd.h>
#include <utility>
#include <vector>
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "compute_extract.hh"
#include "container_mesh.hh"
#include "geometry_manager.hh"
#include "reference_manager_interface.hh"
#include "communicator.hh"
#include "domain_dd_interface.hh"
#include "ref_point_data.hh"
#include "stimulation_zero.hh"
/* -------------------------------------------------------------------------- */
__BEGIN_LIBMULTISCALE__
/* -------------------------------------------------------------------------- */
CADD_template::CADD_template(const std::string &name)
: LMObject(name), CouplingDDMD(name),
pad_atoms(createOutput("pad-" + name)),
core_template("core_template-" + name) {
this->detect_freq = 50;
this->num_close_loop_in_infinity = 0;
this->close_in_infinity = false;
this->multiple_loops = false;
this->detect_dxa = false;
this->detect_partial = false;
this->detect_simple_bool = false;
}
/* -------------------------------------------------------------------------- */
CADD_template::~CADD_template() { clearAll(); }
/* -------------------------------------------------------------------------- */
void CADD_template::mergeDuplicateNodes() {
LM_TOIMPLEMENT;
// for (auto &&nd : this->domA.getContainer().getContainerNodes()) {
// LM_TOIMPLEMENT;
// // UInt nbrs = nd.getNbrOfNeighs();
// UInt nbrs = std::numeric_limits<UInt>::max();
// UInt idx = nd.getIndex();
// if (this->md_cube->contains(nd.position()) && nbrs == 2) {
// UInt idx2 = nd.getIndex();
// for (auto &&nd2 [[gnu::unused]] :
// this->domA.getContainer().getContainerNodes()) {
// LM_TOIMPLEMENT;
// // auto dX = nd.position() - nd2.position();
// // Real dist = dX.norm();
// auto dist = std::numeric_limits<Real>::max();
// if ((idx != idx2) && (dist < 1e-2)) {
// this->domA.freenodearm(idx);
// this->domA.freenode(idx);
// this->domA.freenodearm(idx2);
// this->domA.freenode(idx2);
// }
// }
// }
// }
}
/* -------------------------------------------------------------------------- */
template <typename DomainDD, typename DomainMD>
void CADD_template::init(DomainDD &domDD, DomainMD &domA [[gnu::unused]]) {
// fabricating the geometries
auto md_geom = GeometryManager::getManager().getGeometry(this->md_geom);
this->md_cube = std::dynamic_pointer_cast<Cube>(md_geom);
auto dd_geom = GeometryManager::getManager().getGeometry(this->dd_geom);
this->dd_cube = std::dynamic_pointer_cast<Cube>(dd_geom);
Vector<3u> xmin(this->md_cube->getXmin());
Vector<3u> xmax(this->md_cube->getXmax());
for (UInt i : {0, 1, 2}) {
xmin[i] -= this->pad_thickness + this->distance_ddline_from_pad;
xmax[i] += this->pad_thickness + this->distance_ddline_from_pad;
}
this->md_cube->setXmin(xmin);
this->md_cube->setXmax(xmax);
// constructing some flags
this->detect_simple_bool = false;
simple_direction = 3;
for (auto &&i : {0, 1, 2}) {
if (this->close_direction[i] != 0)
this->close_in_infinity = true;
if (this->detect_simple_direction[i] != 0) {
this->detect_simple_bool = true;
simple_direction = i;
}
}
if (this->aniso) {
Real c11 = this->elastic_coefs[0];
Real c12 = this->elastic_coefs[1];
Real c22 = this->elastic_coefs[4];
Real c66 = this->elastic_coefs[12];
Real lambda2 = sqrt(c11 / c22);
this->c11bar = sqrt(c11 * c12);
this->lambda = sqrt(lambda2);
this->phi = 0.5 * acos((c12 * c12 + 2.0 * c12 * c66 - c11bar * c11bar) /
(2.0 * c11bar * c66));
// if (this->volterra)
// LM_FATAL("cannot be used at the same time with volterra and aniso");
}
// sending infos on parameters to paradis
// Real maxSeg, burgMag;
if (this->is_in_dd) {
auto domDD_xmin = domDD.getDomainBoundingBox().getXmin();
auto domDD_xmax = domDD.getDomainBoundingBox().getXmax();
if ((this->dd_cube->getXmin() - domDD_xmin).norm() > 1e-7 ||
(this->dd_cube->getXmax() - domDD_xmax).norm() > 1e-7) {
DUMP("Check DD box lengths defined Libmultiscale configuration "
"and ParaDis control files",
DBG_WARNING);
DUMP("\n" << *this->dd_cube << " =?= " << domDD.getDomainBoundingBox(),
DBG_WARNING);
}
// this->burgMag0 = domDD.getBurgersMagnitude() * 1e+10; // in angstrom unit
// int my_rank_in_DDprocs = this->comm_group_dd.getMyRank();
// if (my_rank_in_DDprocs == 0) {
// UInt mdNBprocs = this->comm_group_atomic.size();
// for (UInt i = 0; i < mdNBprocs; ++i) {
// this->comm_group_atomic.send(&maxSeg, 1, i, "maximum length of dd");
// this->comm_group_atomic.send(&burgMag, 1, i,
// "burgers vector magnitude");
// }
// }
}
// receive information on parameters to paradis
// if (this->is_in_atomic) {
// this->comm_group_dd.receive(&maxSeg, 1, 0, "maximum length of dd
// segments"); this->comm_group_dd.receive(&burgMag, 1, 0, "burgers vector
// magnitude");
// // this->maxSeg0 = maxSeg;
// // this->burgMag0 = burgMag;
// }
}
/* -------------------------------------------------------------------------- */
template <typename DomainDD, typename DomainMD>
void CADD_template::coupling(CouplingStage stage, DomainDD &domDD,
DomainMD &domA) {
//! Main coupling calls
if (stage == COUPLING_STEP3) {
STARTTIMER("applyDetection");
this->applyDetection(domDD, domA);
STOPTIMER("applyDetection");
STARTTIMER("applyTemplateToPad");
this->applyTemplateToPad(domDD, domA);
STOPTIMER("applyTemplateToPad");
return;
}
}
/* ------------------------------------------------------------------------ */
template <typename DomainDD, typename DomainMD>
void CADD_template::applyDetection(DomainDD &domDD,
DomainMD &domA [[gnu::unused]]) {
if (current_step % this->detect_freq != 0)
return;
auto &all_group [[gnu::unused]] =
Communicator::getCommunicator().getGroup("all");
if (this->is_in_atomic) {
STARTTIMER("detect_cores_by_compute");
auto local_dislocations = this->detect_cores_by_compute();
STOPTIMER("detect_cores_by_compute");
ContainerGenericDDMesh<3> dislocations("detected_dislocations");
LM_TOIMPLEMENT;
// comm_group_A.gather(local_dislocations, dislocations, 0,
// "Gather the detected dislocations");
if (this->comm_group_atomic.getMyRank() == 0) {
LM_TOIMPLEMENT;
// auto root_proc_DD = this->comm_group_dd[0].getAbsoluteRank();
// all_group.send(dislocations, root_proc_DD,
// "Send the detected segments to root of group_DD");
}
}
if (this->is_in_dd) {
STARTTIMER("receiveDetectedSegments");
ContainerGenericDDMesh<3> dislocations("detected_dislocations");
LM_TOIMPLEMENT;
// auto root_proc_MD = this->comm_group_atomic[0].getAbsoluteRank();
// all_group.receive(dislocations, root_proc_MD,
// "Receive the detected segments from root of group_MD");
STOPTIMER("receiveDetectedSegments");
STARTTIMER("moveSlavedDDNodes");
/* loop over the dislocation nodes and treat the pinned ones
in the coupling region, as they have to be moved to match
the detected ones
*/
for (auto nd : domDD.getContainer().getContainerNodes()) {
if (nd.getConstraint() != DomainParaDiS::_fixed)
continue;
// auto &&dd_node = nd.position();
if (not this->md_cube->contains(nd.position()))
continue;
Vector<3> new_position = Vector<3>::Zero();
UInt counter = 0.0;
for (auto &&detected_node : dislocations.getContainerNodes()) {
auto &&detected_pos = detected_node.position();
Real dist = (detected_pos - Vector<3>(nd.position())).norm();
if (dist < this->position_tolerance) {
new_position += detected_pos;
counter += 1.0;
}
if (counter == 0.0)
LM_FATAL(
"cannot find the a detected node close by: " << nd.position());
new_position /= counter;
nd.position() = new_position;
}
nd.getConstraint() = DomainParaDiS::_free;
}
}
STOPTIMER("moveSlavedDDNodes");
STARTTIMER("DDremesh_in_CADD3d");
domDD.remesh();
STOPTIMER("DDremesh_in_CADD3d");
// release or constrain again after coupling step
for (auto &&nd : domDD.getContainer()) {
if (!detect_simple_bool) {
if (this->md_cube->contains(nd.position())) {
nd.getConstraint() = DomainParaDiS::_fixed;
} else {
LM_TOIMPLEMENT;
// UInt nbrs = nd.getNbrOfNeighs();
// if (nbrs == 2)
// nd.unconstrain();
}
}
}
}
/* -------------------------------------------------------------------------- */
ContainerGenericDDMesh<3> CADD_template::detect_cores_by_compute() {
LMID id = compute_list[0];
auto &&my_compute = FilterManager::getManager().getObject(id);
my_compute.compute();
ContainerArray<Real> dislo_pos;
Vector<3u> xmin(this->md_cube->getXmin());
Vector<3u> xmax(this->md_cube->getXmax());
Real pt = this->distance_ddline_from_pad;
xmin -= pt;
xmax += pt;
Cube dd_cube_extended(3);
dd_cube_extended.setXmin(xmin);
dd_cube_extended.setXmax(xmax);
auto segments = my_compute.evalOutput<ContainerGenericDDMesh<3>>();
if (this->detect_simple_bool)
return segments;
FilterGeometry geom_filter("deteced_and_filtered");
geom_filter.setParam("GEOMETRY", dd_cube_extended);
geom_filter.compute(segments);
return geom_filter.evalOutput<ContainerGenericDDMesh<3>>();
}
/* -------------------------------------------------------------------------- */
template <typename DomainDD, typename DomainA>
void CADD_template::applyTemplateToPad(DomainDD &domDD [[gnu::unused]],
DomainA &domA) {
FilterGeometry pad_atoms("pad_atoms");
pad_atoms.setParam("GEOMETTRY", pad_geom);
pad_atoms.compute(domA.getContainer());
// set to zero force and velocity of the pad
StimulationZero stimulation_zero("zero pad forces and velocities");
stimulation_zero.setParam("FIELD", _velocity);
stimulation_zero.compute(pad_atoms);
stimulation_zero.setParam("FIELD", _force);
stimulation_zero.compute(pad_atoms);
// if not the right moment, do nothing
if (current_step % detect_freq != 0)
return;
// retreive the dd_points (communication)
auto dd_points = this->sendDDSegments2MD(domA);
if (this->is_in_atomic) {
STARTTIMER("applyDisplacement");
UInt num_dislo = 0;
std::vector<std::vector<Real>> dislocations;
// Here, we analyze dd_points data to find out
// where and how the cores exist
// if (this->aniso) {
// for (auto &dd_pt : dd_points) {
// for (UInt j = 0; j < 2; ++j) {
// auto neigh = dd_pt.neighs[j];
// auto burg = neigh.burg * this->burgMag0;
// Real bmag =
// burg[0] * cos(this->angle_between_b_and_x / 180.0 * M_PI) +
// burg[2] * sin(this->angle_between_b_and_x / 180.0 * M_PI);
// bmag = roundf(bmag * 10.0) / 10.0;
// if (bmag / fabs(bmag) > 0) {
// Real idx = dd_pt.idx;
// auto &ddX = dd_pt.X;
// Real nbi = neigh.idx;
// if ((idx >= 0.0) && (nbi < 0.0)) {
// num_dislo += 1;
// std::vector<Real> coord_vector;
// coord_vector.push_back(ddX[0]);
// coord_vector.push_back(ddX[1]);
// coord_vector.push_back(ddX[2]);
// dislocations.push_back(coord_vector);
// }
// }
// }
// }
// DUMP(num_dislo << " straight dislocations will be imposed",
// DBG_MESSAGE); for (UInt i = 0; i < num_dislo; ++i) {
// DUMP("(" << dislocations[i][0] << ", " << dislocations[i][1] << ", "
// << dislocations[i][2] << ")",
// DBG_MESSAGE);
// }
// }
auto md_ranges = this->md_cube->getXmax() - this->md_cube->getXmin();
Real mdxlen = md_ranges[0];
Real mdzlen = md_ranges[2];
UInt count_true_disp = 0;
for (auto &&at : this->pad_atoms.get<typename DomainA::ContainerSubset>()) {
auto &&disp = at.displacement();
auto &&pos0 = at.position0();
auto &&pos0_pbc = at.position0();
auto &&pos0_pbc0 = at.position0();
if (fabs(disp[2]) > mdzlen / 2.) {
Real signZ = disp[2] / fabs(disp[2]);
pos0_pbc[2] = pos0[2] + mdzlen * signZ;
count_true_disp += 1;
}
if (fabs(disp[0]) > mdxlen / 2.) {
Real signX = disp[0] / fabs(disp[0]);
pos0_pbc[0] = pos0[0] + mdxlen * signX;
}
for (UInt i = 0; i < 3; ++i) {
pos0_pbc0[i] = pos0_pbc[i];
}
if (this->aniso)
functionifAniso(num_dislo, pos0_pbc, pos0_pbc0, dislocations, at);
// else if (this->volterra)
// functionifVolterra(num_dislo, pos0_pbc, pos0_pbc0, dislocations, at);
else
functionElse(dd_points, pos0_pbc, at);
}
STOPTIMER("applyDisplacement");
// this->domC.updatePAD();
} // if(is_in_atomic ...)
}
/* --------------------------------------------------------------------------
*/
void CADD_template::ComputeAnalyticDisplacements(
Vector<3> &analytic_disp, Vector<3> &B [[gnu::unused]],
Vector<3> &A [[gnu::unused]], Vector<3> &burg [[gnu::unused]],
Vector<3> &pos0
[[gnu::unused]]) { // Compute closure point for given AB segment
Vector<3> C [[gnu::unused]] = Vector<3>::Zero();
LM_TOIMPLEMENT;
// this->domA.computeClosurePoint(B, A, burg, C);
// Compute point P1, projected point of C on line consisted to A and Ab
Vector<3> P1 [[gnu::unused]] = Vector<3>::Zero();
LM_TOIMPLEMENT;
// this->domA.computeProjectedPoint(B, C, burg, P1);
// Compute point P2, projected point of C on line consisted to B and Bb
Vector<3> P2 [[gnu::unused]] = Vector<3>::Zero();
LM_TOIMPLEMENT;
// this->domA.computeProjectedPoint(A, C, burg, P2);
Vector<3> dispPointU1 = Vector<3>::Zero();
LM_TOIMPLEMENT;
// this->domA.computeBarnett(P2, B, A, burg, dispPointU1, pos0,
// this->pois0);
analytic_disp += dispPointU1;
Vector<3> dispPointU2 = Vector<3>::Zero();
LM_TOIMPLEMENT;
// this->domA.computeBarnett(P1, B, P2, burg, dispPointU2, pos0,
// this->pois0);
analytic_disp += dispPointU2;
}
/* --------------------------------------------------------------------------
*/
void CADD_template::ComputeOffsetParallelCurve(
ContainerGenericDDMesh<3> &dd_segments [[gnu::unused]]) {
LM_TOIMPLEMENT;
// for (auto eldd_pt : dd_points) {
// std::vector<Real> angles;
// Real count = 2.0;
// auto node_X = dd_pt.X;
// UInt slip_case = 3;
// Real avgangle = 0.;
// // computes some angle made by the arm in the slip plane
// for (auto &&neigh : dd_pt.neighs) {
// auto &next_X = neigh.X;
// // auto next_normal = neigh.normal;
// Real angle_between_neighbor = 0.0;
// Vector<3> segment = next_X - node_X;
// // should construct a rotation which puts
// // the normal as the direction Z
// LM_TOIMPLEMENT;
// angle_between_neighbor = atan2(segment[0], segment[1]);
// angles.push_back(angle_between_neighbor);
// avgangle += angle_between_neighbor;
// }
// avgangle /= dd_pt.neighs.size();
// Vector<3> dX1 = {0.0, 0.0, 0.0};
// Vector<3> dX2 = {0.0, 0.0, 0.0};
// computeOffset(node_X, avgangle, dX1, dX2, slip_case);
// if (count != 0.0) {
// if (isnan(dX1[0]) || isnan(dX1[1]) || isnan(dX1[2]) || isnan(dX2[0]) ||
// isnan(dX2[1]) || isnan(dX2[2]))
// LM_FATAL("wrong dX1 and dX2");
// }
// angles[0] = radian2degree(angles[0]);
// angles[1] = radian2degree(angles[1]);
// avgangle = (angles[0] + angles[1]) / count;
// findCorrectOffsets(avgangle, dX1, dX2, slip_case);
// dd_pt.tmp_X = node_X + dX1;
// dd_pt.tmp2_X = node_X + dX2;
// }
// return;
}
/* --------------------------------------------------------------------------
*/
void CADD_template::computeOffset(Vector<3> &current_node [[gnu::unused]],
Real ang, Vector<3> &dX1, Vector<3> &dX2,
UInt slip_case) {
Real rmax = this->core_rcut;
Real slope = tan(ang);
Real val1 = sqrt(rmax * rmax / (1.0 + slope * slope));
Real val2 = sqrt(rmax * rmax - rmax * rmax / (1.0 + slope * slope));
Real val3 = -sqrt(rmax * rmax / (1.0 + slope * slope));
Real val4 = -sqrt(rmax * rmax - rmax * rmax / (1.0 + slope * slope));
if (slip_case == 0) {
dX1[0] = 0.0;
dX1[1] = val1;
dX1[2] = val2;
dX2[0] = 0.0;
dX2[1] = val3;
dX2[2] = val4;
} else if (slip_case == 1) {
dX1[0] = val1;
dX1[1] = 0.0;
dX1[2] = val2;
dX2[0] = val3;
dX2[1] = 0.0;
dX2[2] = val4;
} else if (slip_case == 2) {
dX1[0] = val1;
dX1[1] = val2;
dX1[2] = 0.0;
dX2[0] = val3;
dX2[1] = val4;
dX2[2] = 0.0;
} else
LM_FATAL("cannot define angle with neighbor");
return;
}
/* --------------------------------------------------------------------------
*/
void CADD_template::findCorrectOffsets(Real ang, Vector<3> &dX1, Vector<3> &dX2,
UInt slip_case) {
Real dev = 1e+5;
Real saved_i = 1.0;
Real saved_k = 1.0;
for (int i = -1; i < 3;) {
for (int k = -1; k < 3;) {
Real tmp = -1.0;
if (slip_case == 0)
tmp = fabs(radian2degree(atan2(dX2[1] * (Real)i, dX2[2] * (Real)k)) -
ang);
else if (slip_case == 1)
tmp = fabs(radian2degree(atan2(dX2[2] * (Real)i, dX2[0] * (Real)k)) -
ang);
else if (slip_case == 2)
tmp = fabs(radian2degree(atan2(dX2[0] * (Real)i, dX2[1] * (Real)k)) -
ang);
else
LM_FATAL("unknown slip_case");
if (tmp < dev) {
dev = tmp;
saved_i = (Real)i;
saved_k = (Real)k;
}
k += 2;
}
i += 2;
}
if (slip_case == 0) {
dX1[0] = dX1[0];
dX1[1] = dX1[1] * saved_i;
dX1[2] = dX1[2] * saved_k;
dX2[0] = dX2[0];
dX2[1] = dX2[1] * saved_i;
dX2[2] = dX2[2] * saved_k;
} else if (slip_case == 1) {
dX1[0] = dX1[0] * saved_k;
dX1[1] = dX1[1];
dX1[2] = dX1[2] * saved_i;
dX2[0] = dX2[0] * saved_k;
dX2[1] = dX2[1];
dX2[2] = dX2[2] * saved_i;
} else if (slip_case == 2) {
dX1[0] = dX1[0] * saved_i;
dX1[1] = dX1[1] * saved_k;
dX1[2] = dX1[2];
dX2[0] = dX2[0] * saved_i;
dX2[1] = dX2[1] * saved_k;
dX2[2] = dX2[2];
}
return;
}
/* --------------------------------------------------------------------------
*/
bool CADD_template::in_side_ellipse(Real pos[], Real rmax, Real rmin,
Real *factor) {
Real eq =
((pos[0] / rmax) * (pos[0] / rmax) + (pos[1] / rmin) * (pos[1] / rmin));
Real val = 1.0 - eq;
if (val >= 0.0) {
*factor = sqrt(eq);
return true;
} else {
*factor = sqrt(eq);
return false;
}
}
/* --------------------------------------------------------------------------
*/
bool CADD_template::is_it_infinite_connection(UInt index0 [[gnu::unused]],
UInt *index0_infinite
[[gnu::unused]]) {
LM_TOIMPLEMENT;
// int int_index0_infinite;
// bool found = this->domA.getPairedIndex(index0, &int_index0_infinite);
// if (found) {
// *index0_infinite = (UInt)int_index0_infinite;
// return true;
// } else
// return false;
}
/* --------------------------------------------------------------------------
*/
template <typename RefAtom>
void CADD_template::shear_strain(RefAtom at, Vector<3> &disp) {
Real eps_yx = this->shear_stresses[1] / this->mu0;
Real eps_yz = this->shear_stresses[3] / this->mu0;
Real eps_xy = this->shear_stresses[0] / this->mu0;
Real eps_zy = this->shear_stresses[2] / this->mu0;
disp[0] = eps_yx * at.position0()[1];
disp[1] = eps_xy * at.position0()[0] + eps_zy * at.position0()[2];
disp[2] = eps_yz * at.position0()[1];
}
/* --------------------------------------------------------------------------
*/
template <typename RefAtom>
void CADD_template::tilt_strain(RefAtom at, Vector<3> &disp) {
// Real LX = this->md_box_lengths[0];
Real LY = this->md_box_lengths[1];
Real LZ = this->md_box_lengths[2];
// Real LY = fabs(this->md_yrange[1]) + fabs(this->md_yrange[0]);
// Real LZ = fabs(this->md_zrange[1]) + fabs(this->md_zrange[0]);
Real eps_xy = this->tilt_stresses[0] / this->mu0;
Real eps_xz = this->tilt_stresses[1] / this->mu0;
Real eps_yz = this->tilt_stresses[2] / this->mu0;
disp[0] = -1.0 * eps_xy * (at.position0()[1] + LY / 2.0) -
1.0 * eps_xz * (at.position0()[2] + LZ / 2.0);
// disp[1] = -1.0*eps_yz*(at.position0(2) + LZ/2.0); // full dislocation
disp[1] = -1.0 * eps_yz * (at.position0()[2] + LZ / 2.0); // half dislocation
disp[2] = 0.0;
}
/* --------------------------------------------------------------------------
*/
template <typename DomainA>
void CADD_template::close_dislocation_loop(
ContainerGenericDDMesh<3> &dd_segments [[gnu::unused]],
ContainerGenericDDMesh<3> &dd_segments_global [[gnu::unused]],
DomainA &domA [[gnu::unused]]) {
LM_TOIMPLEMENT;
// bool added_point_in_infinity(false);
// Real maxdist = domA.getMaxSeg();
// UInt count = 0;
// for (auto &dd_pt_global : dd_points_global) {
// Real idx_real = dd_pt_global.idx;
// Real num_neigh = dd_pt_global.num_neigh;
// auto crr_X = dd_pt_global.X;
// auto &dd_pt = dd_points[count];
// dd_pt.idx = idx_real;
// dd_pt.num_neigh = num_neigh;
// dd_pt.X = crr_X;
// for (UInt n = 0; n < num_neigh; ++n) {
// auto neigh = dd_pt_global.neighs[n];
// Real nbr_idx = neigh.idx;
// auto &nbr_X = neigh.X;
// auto burg = neigh.burg;
// auto normal = neigh.normal;
// auto diff = nbr_X - crr_X;
// Real dist = diff.norm();
// UInt nbr_idx0_infinite = 0;
// Real nbr_idx_infinite_real = 0.0;
// bool found(false);
// UInt idx0 = (UInt)dd_pt_global.idx0;
// if ((this->close_in_infinity) &&
// (is_it_infinite_connection(idx0, &nbr_idx0_infinite)) &&
// (num_neigh == 1)) {
// // connectivity over an infinity boundary for single neighbors nodes:
// // frank-read sources
// Vector<3> nbr_X_infinite;
// for (auto &dd_pt_global2 : dd_points_global) {
// Real tmp = dd_pt_global2.idx0;
// UInt nbnbr = dd_pt_global2.num_neigh;
// if (((tmp - nbr_idx0_infinite) < 1e-2) && (nbnbr == 1)) {
// nbr_idx_infinite_real = dd_pt_global2.idx;
// nbr_X_infinite = dd_pt_global2.X;
// found = true;
// }
// }
// if (!found)
// LM_FATAL("not found a node infinitely connected for idx0 " <<
// idx0);
// DUMP("infinity points are considered between "
// << idx0 << " (" << crr_X << ") and " << nbr_idx0_infinite
// << " (" << nbr_X_infinite << ") in ("
// << this->close_direction[0] << ", " <<
// this->close_direction[1]
// << ", " << this->close_direction[2] << ") direction",
// DBG_MESSAGE);
// dd_pt.num_neigh = 2;
// dd_pt.neighs[0].idx = nbr_idx;
// dd_pt.neighs[0].X = nbr_X;
// dd_pt.neighs[0].burg = burg;
// dd_pt.neighs[0].normal = normal;
// dd_pt.neighs[1].idx = -1.0 * idx_real - 10000.0;
// for (UInt i : {0, 1, 2}) {
// dd_pt.neighs[1].X[i] = crr_X[i] + this->close_direction[i] * 1E+3;
// }
// dd_pt.neighs[1].burg = burg * -1.;
// dd_pt.neighs[1].normal = normal;
// auto &dd_pt_plus1 = *((&dd_pt) + 1);
// dd_pt_plus1.idx = -1.0 * idx_real - 10000.0;
// dd_pt_plus1.num_neigh = 2;
// for (UInt i : {0, 1, 2}) {
// dd_pt_plus1.X[i] = crr_X[i] + this->close_direction[i] * 1E+3;
// }
// dd_pt_plus1.neighs[0].idx = idx_real;
// dd_pt_plus1.neighs[0].X = crr_X;
// dd_pt_plus1.neighs[0].burg = burg;
// dd_pt_plus1.neighs[0].normal = normal;
// dd_pt_plus1.neighs[1].idx = -1.0 * nbr_idx_infinite_real - 10000.0;
// for (UInt i : {0, 1, 2}) {
// dd_pt_plus1.neighs[1].X[i] =
// nbr_X_infinite[i] + close_direction[i] * 1E+3;
// }
// dd_pt_plus1.neighs[1].burg = burg * -1.;
// dd_pt_plus1.neighs[1].normal = normal;
// added_point_in_infinity = true;
// } else if ((close_in_infinity) && (num_neigh == 2) &&
// (dist > 5.0 * maxdist)) {
// // connectivity over an infinity boundary for Real neighbors nodes:
// // straight dislocation
// dd_pt.neighs[n].idx = -1.0 * dd_pt.idx - 10000.0;
// for (UInt i : {0, 1, 2}) {
// dd_pt.neighs[n].X[i] = crr_X[i] + this->close_direction[i] * 1E+3;
// }
// dd_pt.neighs[n].burg = burg;
// dd_pt.neighs[n].normal = normal;
// auto &dd_pt_plus1 = *((&dd_pt) + 1);
// dd_pt_plus1.idx = -1.0 * dd_pt.idx - 10000.0;
// dd_pt_plus1.num_neigh = 2;
// for (auto i : {0, 1, 2}) {
// dd_pt_plus1.X[i] = crr_X[i] + this->close_direction[i] * 1E+3;
// }
// dd_pt_plus1.idx0 = dd_pt.idx;
// dd_pt_plus1.neighs[0].X = crr_X;
// dd_pt_plus1.neighs[0].burg = burg * -1.;
// dd_pt_plus1.neighs[0].normal = normal;
// dd_pt_plus1.neighs[1].idx = -1.0 * nbr_idx - 10000.0;
// for (auto i : {0, 1, 2}) {
// dd_pt_plus1.neighs[1].X[i] =
// nbr_X[i] + this->close_direction[i] * 1E+3;
// }
// dd_pt_plus1.neighs[1].burg = burg;
// dd_pt_plus1.neighs[1].normal = normal;
// DUMP("Implicit infinity connection between "
// << idx0 << " (" << dd_pt.X << ") and " << dd_pt_plus1.X << "
// ("
// << nbr_X << ").",
// DBG_MESSAGE);
// added_point_in_infinity = true;
// } else if (num_neigh == 1) {
// // && (this->detect_partial)) {
// // straight connection between two single neighbor node: partial
// // detection
// dd_pt.neighs[0].idx = nbr_idx;
// dd_pt.neighs[0].X = nbr_X;
// dd_pt.neighs[0].burg = burg;
// dd_pt.neighs[0].normal = normal;
// Real temp_val = 1e+4;
// // IteratorDDNodes nodes_loop3 =
// // this->domA.getContainerNodes().getIterator();
// // Real tmpx,tmpy,tmpz;
// for (auto &dd_pt_global2 : dd_points_global) {
// UInt num_neigh0 = dd_pt_global2.num_neigh;
// auto diff = dd_pt_global2.X - crr_X;
// Real dist0 = diff.norm();
// if ( // has to be terminated in MD domain
// (num_neigh0 == 1) &&
// // avoid self selection
// (0.1 < dist0) &&
// // find the node minimum distance
// (dist0 < temp_val) &&
// // avoid chosing the node in different slip plane
// (fabs(dd_pt_global2.X[1] - crr_X[1]) < 1.0)) {
// temp_val = dist;
// dd_pt.neighs[0].idx = dd_pt_global2.idx;
// dd_pt.neighs[0].X = dd_pt_global2.X;
// DUMP("Straight connection between the two fixed DD nodes "
// << "(" << dd_pt.X << ") and "
// << "(" << dd_pt_global2.X << ").",
// DBG_MESSAGE);
// }
// }
// dd_pt.num_neigh = 2.0;
// dd_pt.neighs[1].burg = -1.0 * dd_pt.neighs[0].burg;
// dd_pt.neighs[1].normal = dd_pt.neighs[0].normal;
// } else {
// // normal connection
// dd_pt.neighs[n].idx = nbr_idx;
// dd_pt.neighs[n].X = nbr_X;
// dd_pt.neighs[n].burg = burg;
// dd_pt.neighs[n].normal = normal;
// }
// }
// if (added_point_in_infinity) {
// count += 2;
// added_point_in_infinity = false;
// } else {
// count += 1;
// }
// }
// return;
}
/* ------------------------------------------------------------------------ */
template <typename DomainA>
UInt CADD_template::CollectingLocalDDpoints(
ContainerGenericDDMesh<3> &dd_points0_local [[gnu::unused]],
DomainA &domA [[gnu::unused]]) {
LM_TOIMPLEMENT;
// bool problem_in_neigh = false;
// int count = 0;
// for (auto &node : domA.getContainer().getContainerNodes()) {
// Real idx;
// idx = (Real)node.getIndex();
// Real idx0;
// idx0 = (Real)node.getIndex0();
// LM_TOIMPLEMENT;
// // int num_neigh = node.getNbrOfNeighs();
// int num_neigh = std::numeric_limits<int>::max();
// Vector<3> crr_X = {roundf(node.position()[0] * 10.0) / 10.0,
// roundf(node.position()[1] * 10.0) / 10.0,
// roundf(node.position()[2] * 10.0) / 10.0};
// if (num_neigh >= 3) {
// DUMP("[collectingLocalDDpoints] problem in DD node idx"
// << idx << " (" << crr_X << "): number of neighbors "
// << num_neigh,
// DBG_MESSAGE);
// num_neigh = 2;
// problem_in_neigh = true;
// }
// dd_points0_local[count].idx = idx;
// dd_points0_local[count].num_neigh = (Real)num_neigh;
// dd_points0_local[count].X = crr_X;
// dd_points0_local[count].idx0 = (Real)idx0;
// for (int n = 0; n < num_neigh; ++n) {
// LM_TOIMPLEMENT;
// // Real nbr_idx = (Real)node.getNeighborNodeIndex(n);
// // Vector<Dim> nbr_X = {
// // roundf(node.getNeighborNodeCoord(n, 0) * 10.0) / 10.0,
// // roundf(node.getNeighborNodeCoord(n, 1) * 10.0) / 10.0,
// // roundf(node.getNeighborNodeCoord(n, 2) * 10.0) / 10.0};
// // Vector<Dim> burg = {node.burgersVectorX(n), node.burgersVectorY(n),
// // node.burgersVectorZ(n)};
// // Vector<Dim> normal = {node.normalVectorX(n), node.normalVectorY(n),
// // node.normalVectorZ(n)};
// // auto &neigh = dd_points0_local[count].neighs[n];
// // neigh.idx = nbr_idx;
// // neigh.burg = burg;
// // neigh.normal = normal;
// }
// }
// if (this->dd_points_out) {
// // Communicator &comm = Communicator::getCommunicator();
// UInt my_rank_in_DD = this->comm_group_dd.getMyRank();
// std::stringstream fname0;
// fname0 << this->directory_dd_points << "/local_dd_points0_step"
// << current_step << ".txt.proc" << my_rank_in_DD;
// std::ofstream myfile0;
// for (auto &dd_pt0 : dd_points0_local) {
// if (!myfile0.is_open()) {
// myfile0.open(fname0.str().c_str());
// }
// myfile0 << dd_pt0 << "\t";
// }
// myfile0.close();
// }
// if (problem_in_neigh)
// return 1;
// else
// return 0;
}
/* --------------------------------------------------------------------------
*/
bool CADD_template::coarse_dislocation_loop(
ContainerGenericDDMesh<3> &dd_points [[gnu::unused]],
ContainerGenericDDMesh<3> &dd_points0 [[gnu::unused]]) {
LM_TOIMPLEMENT;
// std::vector<UInt> indices;
// Vector<3> burg = {0.0, 0.0, 0.0};
// Vector<3> norm = {0.0, 0.0, 0.0};
// Real ci = 0.;
// UInt count = 0;
// UInt master_count = 0;
// for (UInt i = 0; i < dd_points0.size(); ++i) {
// if (!(std::find(indices.begin(), indices.end(), i) != indices.end())) {
// auto &dd_pt = dd_points0[i];
// Real ci0 = dd_pt.idx;
// auto cp0 = dd_pt.X;
// if (master_count >= dd_points0.size()) {
// DUMP("[coarse_dislocation_loop] exit 1", DBG_MESSAGE);
// return false;
// }
// auto &dd_pt_master = dd_points[master_count];
// auto &dd_pt_master_1 = dd_points[master_count - 1];
// auto &dd_pt_master_2 = dd_points[master_count - count];
// dd_pt_master.idx = ci0; // idx
// dd_pt_master.num_neigh = 2.0; // #nbr
// dd_pt_master.X = cp0; // x-coord
// master_count += 1;
// count += 1;
// ci = ci0;
// Vector<3> cp = cp0;
// indices.push_back(i);
// bool loop_start(true);
// bool not_finish(true);
// while (not_finish) {
// if (!loop_start) {
// if (is_it_closed(cp, cp0, ci, ci0)) {
// not_finish = false;
// if (master_count - 1 >= dd_points0.size()) {
// DUMP("[coarse_dislocation_loop] exit 2", DBG_MESSAGE);
// return false;
// }
// dd_pt_master_1.neighs[0].idx = ci0; // idx-nbr1
// dd_pt_master_1.neighs[0].X = cp0; // x-coord-nbr1
// dd_pt_master_1.neighs[0].burg = burg; // bx-nbr1
// dd_pt_master_1.neighs[0].normal = norm; // bx-nbr1
// dd_pt_master_2.neighs[1].idx = dd_pt_master_1.idx; // idx-nbr2
// dd_pt_master_2.neighs[1].X = dd_pt_master_1.X; //
// x-coord-nbr2 dd_pt_master_2.neighs[1].burg = -1.0 * burg; //
// bx-nbr2 dd_pt_master_2.neighs[1].normal = norm; //
// bx-nbr2 count = 0; break;
// }
// }
// UInt count_inside = 0;
// bool straight(true);
// while (straight) {
// auto n1 = dd_pt.neighs[0].X;
// Real b1mag = dd_pt.neighs[0].burg[0] *
// cos(this->angle_between_b_and_x / 180.0 * M_PI) +
// dd_pt.neighs[0].burg[2] *
// sin(this->angle_between_b_and_x / 180.0 * M_PI);
// Real n1idx_real = dd_pt.neighs[1].idx;
// UInt n1idx = (UInt)n1idx_real;
// auto n2 = dd_pt.neighs[1].X;
// Real b2mag = dd_pt.neighs[1].burg[0] *
// cos(this->angle_between_b_and_x / 180.0 * M_PI) +
// dd_pt.neighs[1].burg[2] *
// sin(this->angle_between_b_and_x / 180.0 * M_PI);
// Real n2idx_real = dd_pt.neighs[2].idx;
// UInt n2idx = (UInt)n2idx_real;
// UInt saved_i = i;
// UInt tmp = find_one_direction(n1, n2, b1mag, b2mag, n1idx, n2idx,
// &i); if ((tmp == 0) && (master_count == 1)) {
// burg = 1.0 * dd_points0[saved_i].neighs[0].burg;
// } else if ((tmp == 1) && (master_count == 1)) {
// burg = -1.0 * dd_points0[saved_i].neighs[0].burg;
// }
// norm = dd_points0[saved_i].neighs[0].normal;
// // check whether this local connection is important or not
// Real area_in_yplane1 = AreaTriangle(cp, n1, n2, 1);
// Vector<3> last_registered_node = dd_pt_master_1.X;
// Vector<3> next_node = dd_pt.X;
// Real area_in_yplane2 =
// AreaTriangle(last_registered_node, cp, next_node, 1);
// if ((area_in_yplane1 > this->min_area) ||
// (area_in_yplane2 > this->min_area) ||
// (fabs(n2[1] - cp[1]) > 1.0) ||
// (fabs(n1[1] - cp[1]) > 1.0)) { // || (dist >
// this->maxSeg0*10.0)){
// if (master_count - 1 >= dd_points0.size() - 1) {
// DUMP("[coarse_dislocation_loop] exit 3", DBG_MESSAGE);
// return false;
// }
// dd_pt_master.neighs[1].idx = dd_pt_master_1.idx; // idx-nbr2
// dd_pt_master.neighs[1].X = dd_pt_master_1.X; // x-coord-nbr2
// dd_pt_master.neighs[1].burg = -1.0 * burg; // bx-nbr2
// dd_pt_master.neighs[1].normal = norm; // nx-nbr2
// dd_pt_master_1.neighs[0].idx = ci; // idx-nbr1
// dd_pt_master_1.neighs[0].X = cp; // x-coord-nbr1
// dd_pt_master_1.neighs[0].burg = 1.0 * burg; // bx-nbr1
// dd_pt_master_1.neighs[0].normal = norm; // bx-nbr1
// if (!loop_start) {
// dd_pt_master.idx = ci; // idx
// dd_pt_master.num_neigh = 2.0; // #nbr
// dd_pt_master.X = cp; // x-coord
// master_count += 1;
// count += 1;
// }
// }
// loop_start = false;
// indices.push_back(i);
// ci = dd_pt.idx;
// cp = dd_pt.X;
// count_inside += 1;
// if (is_it_closed(cp, cp0, ci, ci0))
// break;
// if (count_inside >= dd_points0.size()) {
// DUMP("[coarse_dislocation_loop] exit 4", DBG_MESSAGE);
// return false;
// }
// }
// if (master_count >= dd_points0.size()) {
// DUMP("[coarse_dislocation_loop] exit 5", DBG_MESSAGE);
// return false;
// }
// }
// if (master_count >= dd_points0.size()) {
// DUMP("[coarse_dislocation_loop] exit 6", DBG_MESSAGE);
// return false;
// }
// }
// if (master_count >= dd_points0.size()) {
// DUMP("[coarse_dislocation_loop] exit 7", DBG_MESSAGE);
// return false;
// }
// }
// return true;
}
/* ------------------------------------------------------------------------ */
template <typename DomainA> void CADD_template::SetPadAtoms(DomainA &domA) {
UInt registered = 0;
int my_rank_in_MDprocs = this->comm_group_atomic.getMyRank();
LM_TOIMPLEMENT;
// this->pad_atoms.clear();
auto ref_manager = domA.getContainer().getRefManager();
auto pad = GeometryManager::getManager().getGeometry(pad_geom);
for (auto &at : domA.getContainer()) {
if (pad->contains(at.position())) {
LM_TOIMPLEMENT;
// this->pad_atoms.push_back(at);
registered += 1;
}
}
this->comm_group_atomic.reduce(&registered, 1, "atoms: registered as PAD",
OP_SUM);
domA.getContainer().addSubSet(this->pad_atoms.get<ContainerInterface>());
if (my_rank_in_MDprocs == 0) {
DUMP(registered << " atoms are registered as pad atoms", DBG_MESSAGE);
}
}
/* --------------------------------------------------------------------------
*/
void CADD_template::find_indices_for_neighbors(
ContainerGenericDDMesh<3> &dd_points [[gnu::unused]]) {
LM_TOIMPLEMENT;
// for (auto &dd_pt : dd_points) {
// if (dd_pt.num_neigh != 2)
// LM_FATAL("this is not a proper dislocation network");
// for (UInt j = 0; j < 2; ++j) {
// auto neigh = dd_pt.neighs[j];
// Real next_idx = neigh.idx;
// Vector<3> next_X = neigh.X;
// UInt k = 0;
// for (auto &dd_pt2 : dd_points) {
// Real node_idx = dd_pt2.idx;
// Vector<3> normal = dd_pt2.X;
// Real dist = (normal - next_X).norm();
// if ((dist < 1e-3) && (fabs(node_idx - next_idx) <= 1e-3)) {
// neigh.idx = (Real)k;
// // break;
// ++k;
// }
// }
// }
// }
}
// -------------------------------------------------------------------------
// */
template <typename RefAtom>
void CADD_template::applyDisplacement(RefAtom at [[gnu::unused]],
Vector<3> &dislo_disp [[gnu::unused]]) {
LM_TOIMPLEMENT;
// auto disp_strain = Vector<3>::Zero();
// auto disp_tilt = Vector<3>::Zero();
// shear_strain(at, disp_strain);
// tilt_strain(at, disp_tilt);
// at.position() = at.position0() + dislo_disp - disp_strain + disp_tilt;
}
/* ------------------------------------------------------------------------ */
void CADD_template::coordinate_for_2d_template(Real angle, Real local_coord[],
Real global_coord[] [
[gnu::unused]],
Real template_coord[]) {
template_coord[0] =
local_coord[0] * local_coord[0] / fabs(local_coord[0] + 1e-8) *
sin(angle * M_PI / 180.0) +
local_coord[2] * local_coord[2] / fabs(local_coord[2] + 1e-8) *
cos(angle * M_PI / 180.0);
template_coord[1] =
local_coord[1] * local_coord[1] / fabs(local_coord[1] + 1e-8);
}
/////// This part below should be removed later
////////////////////////////////////
/* ------------------------------------------------------------------------ */
inline Real CADD_template::eval_q_coef(Real *X) {
Real inside = X[0] * X[0] +
2.0 * X[0] * X[1] * this->lambda * cos(this->phi) +
X[1] * X[1] * this->lambda * this->lambda;
return sqrt(inside);
}
/* ------------------------------------------------------------------------ */
inline Real CADD_template::eval_t_coef(Real *X) {
Real inside = X[0] * X[0] -
2.0 * X[0] * X[1] * this->lambda * cos(this->phi) +
X[1] * X[1] * this->lambda * this->lambda;
return sqrt(inside);
}
/* ------------------------------------------------------------------------ */
inline Real CADD_template::evalArctan(Real X, Real Y) {
if ((X >= 0.0) && (Y > 0.0)) {
return atan(Y / X);
} else if ((X < 0.0) && (Y >= 0.0)) {
return atan(-Y / -X) + M_PI;
} else if ((X < 0.0) && (Y < 0.0)) {
return atan(-Y / -X) - M_PI;
} else if ((X >= 0.0) && (Y < 0.0)) {
return atan(Y / X);
} else {
LM_FATAL("unexpected case");
}
}
/* ------------------------------------------------------------------------ */
inline Real CADD_template::computeBeDisplacementAniso(Real b_edge, Real *X) {
Real q = eval_q_coef(X);
Real t = eval_t_coef(X);
Real c12 = this->elastic_coefs[1];
Real c66 = this->elastic_coefs[12];
Real first = 0.0;
first += evalArctan((X[0] + X[1] * this->lambda * cos(this->phi)),
(X[1] * this->lambda * sin(this->phi)));
first += evalArctan((X[0] - X[1] * this->lambda * cos(this->phi)),
(X[1] * this->lambda * sin(this->phi)));
Real second = (this->c11bar * this->c11bar - c12 * c12) /
(2.0 * this->c11bar * c66 * sin(2.0 * this->phi)) *
floor(log(q / t) * 1e+5) / 1e+5;
Real res = b_edge / (4.0 * M_PI) * (first + second);
return res;
}
/* ------------------------------------------------------------------------ */
inline Real CADD_template::computeNDisplacementAniso(Real b_edge, Real *X) {
Real q = eval_q_coef(X);
Real t = eval_t_coef(X);
Real c12 = this->elastic_coefs[1];
Real first =
(this->c11bar - c12) * cos(this->phi) * floor(log(q * t) * 1e+5) / 1e+5;
Real second =
(this->c11bar + c12) * sin(this->phi) *
atan2((X[1] * X[1] * this->lambda * this->lambda * sin(2.0 * this->phi)),
(X[0] * X[0] -
this->lambda * this->lambda * X[1] * X[1] * cos(2.0 * this->phi)));
Real res = -this->lambda * b_edge /
(4.0 * M_PI * this->c11bar * sin(2.0 * this->phi)) *
(first - second);
return res;
}
/* ------------------------------------------------------------------------ */
inline Real CADD_template::computeLDisplacementAniso(Real b_screw, Real *X) {
Real c44 = this->elastic_coefs[9];
Real c45 = this->elastic_coefs[10];
Real c55 = this->elastic_coefs[11];
Real res = b_screw / 2.0 / M_PI *
atan2(sqrt(c44 * c55 - c45 * c45) * X[1], c44 * X[0] - c45 * X[1]);
return res;
}
/* --------------------------------------------------------------------------
*/
inline Real CADD_template::computeBeDisplacement(Real b_edge, Real *X) {
Real x_2 = X[0] * X[0];
Real y_2 = X[1] * X[1];
Real nu = this->pois0;
Real epsilon = 1e-15;
Real res = b_edge / (2.0 * M_PI) *
(atan2(X[1], X[0]) +
X[0] * X[1] / (2.0 * (1 - nu) * (x_2 + y_2 + epsilon)));
return res;
}
/* --------------------------------------------------------------------------
*/
inline Real CADD_template::computeNDisplacement(Real b_edge, Real *X) {
Real x_2 = X[0] * X[0];
Real y_2 = X[1] * X[1];
Real epsilon = 1e-15;
Real nu = this->pois0;
// Real b_edge2 = b_edge*b_edge;
// Real res = -b_edge/(2.0*M_PI)*((1-2.0*nu)/(4.0*(1-nu))*(log((x_2 +
// y_2+epsilon)/b_edge2))
// + (x_2 - y_2)/
// (4.0*(1-nu)*(x_2 + y_2+epsilon))
// );
// return res;
return -b_edge / (2.0 * M_PI) *
((1 - 2.0 * nu) / (4.0 * (1 - nu)) * log(x_2 + y_2 + epsilon) +
(x_2 - y_2) / (4.0 * (1 - nu) * (x_2 + y_2 + epsilon)));
}
/* --------------------------------------------------------------------------
*/
inline Real CADD_template::computeLDisplacement(Real b_screw, Real *X) {
Real res = b_screw / (2.0 * M_PI) * (atan2(X[1], X[0]));
return res;
}
/* --------------------------------------------------------------------------
*/
/* LMDESC CADDTEMPLATE
CADDTEMPLATE is a simple CADD-like coupler which can deal with straing egde
dislocations normal to the interface (i.e., 1;3400;0cno elastic problem)
*/
/* LMEXAMPLE
COUPLING_CODE
*/
void CADD_template::declareParams() {
/* LMKEYWORD PAD_GEOM
Identifier of the pad geom
*/
this->parseKeyword("PAD_GEOM", this->pad_geom);
/* LMKEYWORD DETECT_DXA
Boolean to notify input from DXA results
*/
this->parseTag("DETECT_DXA", this->detect_dxa, false);
/* LMKEYWORD MULTIPLE_LOOPS
Boolean to notify multiple loops
*/
this->parseTag("MULTIPLE_LOOPS", this->multiple_loops, false);
/* LMKEYWORD DETECT_PARTIAL
Boolean to notify input for partially detection dislocation
*/
this->parseTag("DETECT_PARTIAL", this->detect_partial, false);
/* LMKEYWORD PRINT_DD_POINTS
Boolean to print internal data named dd\_points and its offset lines
*/
this->parseTag("PRINT_DD_POINTS", this->dd_points_out, false);
/* LMKEYWORD COARSEN_DD
Boolean to decide to coarse dislocation loop before apply Barnett fields
*/
this->parseTag("COARSEN_DD", this->coarsen_dd, false);
/* LMKEYWORD MIN_AREA
Specifies the minimum area to be coarsed out
*/
this->parseKeyword("MIN_AREA", this->min_area, 0.0);
/* LMKEYWORD DIRECTORY_DD_POINTS
fies the directory dd points are written
*/
this->parseKeyword("DIRECTORY_DD_POINTS", this->directory_dd_points, "/.");
/* LMKEYWORD DD_BOUNDARY
Boolean to set DD boundary
*/
this->parseTag("DD_BOUNDARY", this->dd_boundary, false);
/* LMKEYWORD SLIP_PLANE
Specifies the normal vector to the slip plane
*/
this->parseVectorKeyword("SLIP_PLANE", 3, this->normal_quant);
/* LMKEYWORD ANGLE_BETWEEN_B_AND_X
Specifies the angle between Burgers vector and X(11-2) axis
*/
this->parseKeyword("ANGLE_BETWEEN_B_AND_X", this->angle_between_b_and_x);
/* LMKEYWORD MD_GEOM
Identifier of the MD geometry
*/
this->parseKeyword("MD_GEOM", this->md_geom);
/* LMKEYWORD DD_GEOM
Identifier of the DD geometry
*/
this->parseKeyword("DD_GEOM", this->dd_geom);
/* LMKEYWORD UPDATE_FREQUENCY
Specifies the frequency at which detection computations are performed
*/
this->parseKeyword("UPDATE_FREQUENCY", this->detect_freq, 50);
/* LMKEYWORD DETECT_COMPUTE
Add computes to input to filter
*/
this->parseKeyword("DETECT_COMPUTE", compute_list);
// /* LMKEYWORD NUM_TEMPLATES
// Specifies the number of templates provided.
// */
// this->parseKeyword("NUM_TEMPLATES", this->num_templates);
// /* LMKEYWORD TEMPLATE_FILE
// Specifies the template file.
// */
// this->parseKeyword("TEMPLATE_FILE", this->templatefilename_list);
/* LMKEYWORD CORE_RCUT
Specifies the radius of the region where core template applied
*/
this->parseKeyword("CORE_RCUT", this->core_rcut);
/* LMKEYWORD RATIO_OF_PURE_CORE_TEMPLATE
Specifies the ratio of region where only pure core template is applied
with respect to ellipse defined by RMAX and RMIN
where
*/
this->parseKeyword("RATIO_OF_PURE_CORE_TEMPLATE", this->factor);
/* LMKEYWORD SHEAR_STRESSES
Specifies the shear stersses with respect to Y plane.
*/
this->parseVectorKeyword("SHEAR_STRESSES", 4, this->shear_stresses);
/* LMKEYWORD TILT_STRESSES
Specifies the shear stersses imposed by tilting.
*/
this->parseVectorKeyword("TILT_STRESSES", 3, this->tilt_stresses);
/* LMKEYWORD SHEAR_MODULUS
Specifies the shear modulus in Burgers vector direction.
mega-Pascal unit
*/
this->parseKeyword("SHEAR_MODULUS", this->mu0);
/* LMKEYWORD POISSON_RATIO
Specifies the shear modulus in Burgers vector direction.
mega-Pascal unit
*/
this->parseKeyword("POISSON_RATIO", this->pois0);
/* LMKEYWORD DOMAIN_DD
Specifies the dd domain id
*/
this->parseKeyword("DOMAIN_DD", this->domdd);
/* LM_KEYWORD DISTANCE_DDLINE_FROM_PAD
Specify the distance from pad (inner) boudaries to DD constrained nodes.
*/
this->parseKeyword("DISTANCE_DDLINE_FROM_PAD",
this->distance_ddline_from_pad);
/* LMKEYWORD PAD_THICKNESS
Specify the thickness of pad zone
*/
this->parseKeyword("PAD_THICKNESS", this->pad_thickness);
// In case of (0, 0, 1):
// point1 point2
// *----------------------*
// | | z
// | | ^
// | | |
// o-o-o-x x-o-o-o-o +--->x
/* LMKEYWORD CLOSE_IN_INFINITY
Specifies the direction in order to close
dislocation loop
*/
this->parseVectorKeyword("CLOSE_IN_INFINITY", 3, this->close_direction,
VEC_DEFAULTS(0, 0, 0));
/* LMKEYWORD NUBER_INFINITY_CONNECTION
Specifies the direction in order to close
dislocation loop
*/
this->parseKeyword("NUBER_INFINITY_CONNECTION",
this->num_close_loop_in_infinity, 0);
/* LMKEYWORD MD_BOX_LENGTHS
Specifies the (original) MD box size
to specify correct tilt stress values
*/
this->parseVectorKeyword("MD_BOX_LENGTHS", 3, this->md_box_lengths);
/* LMKEYWORD DETECT_SIMPLE_DIRECTION
Specifies the (original) MD box size
to specify correct tilt stress values
*/
this->parseVectorKeyword("DETECT_SIMPLE_DIRECTION", 3,
this->detect_simple_direction,
VEC_DEFAULTS(0, 0, 0));
// /* LM**KEYWORD NB_SLIPS
// Specifies the number of slip planes
// */
// this->parseKeyword("NB_SLIPS", this->nb_slips);
// /* LM**KEYWORD LEN_INTER_SLIPS
// Specifies length between two slip planes
// */
// this->parseKeyword("LEN_INTER_SLIPS", this->len_inter_slips);
/* LMKEYWORD ANISO
Boolean to set anisotropicity:
[1] Elastic Strain Fields and Dislocation Mobility, Volume 31, J.Lothe
Dislocations in anisotropic media
[2] Plane-strain discrete dislocation plasticity incorporating
anisotropic
elasticity IJSS 48(2), January 2011
*/
this->parseTag("ANISO", this->aniso, false);
// /* LMKEYWORD VOLTERRA
// TODO
// */
// this->parseTag("VOLTERRA", this->volterra, false);
/* LMKEYWORD POSITION_TOLERANCE
The position of the nodes found from the detection
have to be imposed to the current dislocation nodes.
This parameter specify the distance tolerance for which nodes
in the previous configuration are mapped to the detected ones.
*/
this->parseKeyword("POSITION_TOLERANCE", this->position_tolerance);
/* LMKEYWORD ELASTIC_COEFS
Specify the thirteen elastic coefficients
of the constitutive matrix of anisotropic media in the following order:
C11, C12, C13, C21, C22, C23, C31, C32, C33, C44, C45, C55, C66
*/
this->parseVectorKeyword("ELASTIC_COEFS", 13, this->elastic_coefs,
VEC_DEFAULTS(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0));
// forward parsable of the core template needs to be done
this->addSubParsableObject(this->core_template);
}
/* ---------------------------------------------------------------------- */
template <typename DomainA>
ContainerGenericDDMesh<3> CADD_template::sendDDSegments2MD(DomainA &domA) {
UInt data_length = 33;
ContainerGenericDDMesh<3> dd_points("dd_points");
ContainerGenericDDMesh<3> dd_points0("dd_points0");
UInt num_dd_pts;
UInt num_dd_pts0;
if (this->is_in_dd) {
LM_TOIMPLEMENT;
// UInt my_rank_in_DD = this->comm_group_dd.groupRank(lm_my_proc_id);
// UInt nbDDprocs = this->comm_group_dd.getNBprocsOnGroup();
// declare data array for (unclosed) dd points in a local processor
// UInt num_dd_pts0_local = domA.getNBNodes();
ContainerGenericDDMesh<3> dd_points0_local(
"dd_points0_local"); //(num_dd_pts0_local);
// collecting dd points
UInt collecting = 0;
collecting = CollectingLocalDDpoints(dd_points0_local, domA);
this->comm_group_dd.reduce(
&collecting, 1,
"boolean to detect any errors occurring in collecting local nodes",
OP_SUM);
// evaluate total number of points
UInt num_dd_pts0_global [[gnu::unused]] = 0;
LM_TOIMPLEMENT;
// MPI_Reduce(&num_dd_pts0_local, &num_dd_pts0_global, 1, MPI_INT,
// MPI_SUM,
// comm.realRank(0, ),
// comm.getMpiGroup(this->comm_group_dd));
// declare data array for (unclosed) total dd points only at 0 processor
ContainerGenericDDMesh<3> dd_points0_global("dd_points0_global");
LM_TOIMPLEMENT;
// if (my_rank_in_DD == 0) {
// (unclosed) total dd points
// dd_points0_global.resize(num_dd_pts0_global);
//}
// declare data list for number of dd nodal points for all the DDD
// processors
std::vector<int> num_dd_pts0_local_list;
LM_TOIMPLEMENT;
// if (my_rank_in_DD == 0)
// num_dd_pts0_local_list.resize(nbDDprocs);
// gather the total local data length into the declared data list
LM_TOIMPLEMENT;
// int local_length = num_dd_pts0_local * local_data_length;
// MPI_Gather(&local_length, 1, MPI_INT, &num_dd_pts0_local_list[0], 1,
// MPI_INT, comm.realRank(0, this->comm_group_dd),
// comm.getMpiGroup(this->comm_group_dd));
// declear data array for starting number
std::vector<int> lengths_dd_points_local;
LM_TOIMPLEMENT;
// UInt totlen = 0;
// if (my_rank_in_DD == 0) {
// lengths_dd_points_local.resize(nbDDprocs);
// lengths_dd_points_local[0] = 0;
// totlen += num_dd_pts0_local_list[0];
// for (UInt i = 1; i < nbDDprocs; i++) {
// lengths_dd_points_local[i] = lengths_dd_points_local[i - 1] +
// totlen; totlen = num_dd_pts0_local_list[i];
// }
// }
// gather local dd points to global points
// inputs: dd_points0_local
// local_length
// num_dd_pts0_local_list
// lengths_dd_points_local
LM_TOIMPLEMENT;
// MPI_Gatherv(&dd_points0_local[0], // local dd points
// local_length, // local length
// MPI_REAL,
// &dd_points0_global[0], // global dd points
// &num_dd_pts0_local_list[0], // number of length list
// &lengths_dd_points_local[0], // starting number list
// MPI_REAL, comm.realRank(0, this->comm_group_dd),
// comm.getMpiGroup(this->comm_group_dd));
LM_TOIMPLEMENT;
int my_rank_in_DD = std::numeric_limits<int>::max();
if (my_rank_in_DD == 0) {
LM_TOIMPLEMENT;
// UInt num_pairs_in_infinity = domA.getNBpairsInfinite();
// UInt num_dd_pts0 = num_dd_pts0_global;
// if (this->close_in_infinity)
// num_dd_pts0 += (num_pairs_in_infinity + num_close_loop_in_infinity) *
// 2;
// (closed) total dd nodal points
LM_TOIMPLEMENT;
// dd_points0.resize(num_dd_pts0);
close_dislocation_loop(dd_points0, dd_points0_global, domA);
find_indices_for_neighbors(dd_points0);
num_dd_pts = num_dd_pts0;
// (closed) coarsened dd nodal points
LM_TOIMPLEMENT;
// dd_points.resize(num_dd_pts);
STARTTIMER("coarsening");
if ((coarsen_dd) and (collecting == 0)) {
bool coarsen = coarse_dislocation_loop(dd_points, dd_points0);
if (!coarsen) {
// in case of fail, copy all dd_points0 into dd_points
DUMP("[WARNING] Fail to coarse dislocation loop. Total "
<< num_dd_pts0 << " points",
DBG_MESSAGE);
for (UInt i = 0; i < num_dd_pts; ++i) {
for (UInt j = 0; j < data_length; ++j) {
dd_points[i * data_length + j] = dd_points0[i * data_length + j];
}
}
} else {
// in case of success,
DUMP("Coarse_dislocation_loop end. Reduced from "
<< num_dd_pts0 << " points to " << num_dd_pts << " points",
DBG_MESSAGE);
find_indices_for_neighbors(dd_points);
}
} else {
DUMP("[WARNING] Coarse_dislocation_loop is skipped. coarsen_dd: "
<< coarsen_dd << " collecting: " << collecting << " Total "
<< num_dd_pts0 << " points",
DBG_MESSAGE);
for (UInt i = 0; i < num_dd_pts; ++i) {
for (UInt j = 0; j < data_length; ++j) {
dd_points[i * data_length + j] = dd_points0[i * data_length + j];
}
}
}
STOPTIMER("coarsening");
ComputeOffsetParallelCurve(dd_points);
// CorrectIntersetingPoints(num_dd_pts,dd_points);
UInt mdNBprocs = this->comm_group_atomic.size();
for (UInt i = 0; i < mdNBprocs; ++i) {
// int real_rank = this->comm_group_atomic.realRank(i);
LM_TOIMPLEMENT;
// int my_rank_in_group = this->comm_group_atomic.getMyRank();
// this->comm_group_atomic.send(&num_dd_pts, 1, my_rank_in_group,
// "number of dd points");
// this->comm_group_atomic.send(&num_dd_pts0, 1, my_rank_in_group,
// "number of dd points0");
// this->comm_group_atomic.send(&(dd_points[0]), num_dd_pts0 *
// data_length,
// my_rank_in_group, "dd_points (vector)");
}
}
// BARRIER
this->comm_group_dd.synchronize();
}
if (this->is_in_atomic) {
STARTTIMER("SetPadAtoms");
SetPadAtoms(domA);
STOPTIMER("SetPadAtoms");
STARTTIMER("ReceiveDDdata");
this->comm_group_dd.receive(&num_dd_pts, 1, 0, "number of dd points");
this->comm_group_dd.receive(&num_dd_pts0, 1, 0, "number of dd points0");
LM_TOIMPLEMENT;
// dd_points.resize(num_dd_pts0 * data_length);
// this->comm_group_dd.receive(&(dd_points[0]), num_dd_pts0 * data_length,
// 0,
// "dd_points (vector)");
STOPTIMER("ReceiveDDdata");
std::vector<UInt> indices;
// UInt count_pad_core = 0;
// UInt count_pad_around = 0;
// UInt count_far_field = 0;
// UInt count_true_disp = 0;
}
return dd_points;
}
/* ---------------------------------------------------------------------- */
template <typename Vec, typename RefAtom>
void CADD_template::functionifAniso(
UInt num_dislo, Vec &pos0_pbc, Vec &pos0_pbc0,
std::vector<std::vector<Real>> &dislocations, RefAtom &at) {
Vector<3> analytic_Disp = {0.0, 0.0, 0.0};
for (UInt nd = 0; nd < num_dislo; ++nd) {
for (UInt pd = 0; pd < 2; ++pd) {
if (pd == 0) {
// pos0_pbc[0] = pos0_pbc0[0] - dislocations[nd][0];
pos0_pbc[1] = pos0_pbc0[1] - dislocations[nd][1];
pos0_pbc[2] = pos0_pbc0[2] - (dislocations[nd][2] - 1.0);
} else if (pd == 1) {
// pos0_pbc[0] = pos0_pbc0[0] - dislocations[nd][0];
pos0_pbc[1] = pos0_pbc0[1] - dislocations[nd][1];
pos0_pbc[2] = pos0_pbc0[2] - (dislocations[nd][2] + 1.0);
}
Real rotation_matrix[] = {0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0};
Real pos0_pbc_rotate[] = {0.0, 0.0, 0.0};
for (UInt i = 0; i < 3; ++i) {
pos0_pbc_rotate[i] = 0.0;
for (UInt j = 0; j < 3; ++j) {
pos0_pbc_rotate[i] += rotation_matrix[3 * i + j] * pos0_pbc[j];
}
}
LM_TOIMPLEMENT;
// Real b_screw =
// this->burgMag0 * cos(this->angle_between_b_and_x / 180.0 * M_PI)
// / 2.;
// Real b_edge =
// this->burgMag0 * sin(this->angle_between_b_and_x / 180.0 * M_PI)
// / 2.;
// Real analytic_disp[] = {0.0, 0.0, 0.0};
// Real dispV[] = {0.0, 0.0, 0.0};
// analytic_disp[0] = computeBeDisplacementAniso(b_edge, pos0_pbc_rotate);
// analytic_disp[1] = computeNDisplacementAniso(b_edge, pos0_pbc_rotate);
// analytic_disp[2] = computeLDisplacementAniso(b_screw, pos0_pbc_rotate);
// for (UInt i = 0; i < 3; ++i) {
// for (UInt j = 0; j < 3; ++j) {
// analytic_Disp[i] += rotation_matrix[3 * j + i] * analytic_disp[j];
// }
// }
// for (UInt i = 0; i < 3; ++i)
// analytic_Disp[i] += dispV[i];
}
}
applyDisplacement(at, analytic_Disp);
}
/* ------------------------------------------------------------------------ */
template <typename RefAtom>
void CADD_template::functionifVolterra(
UInt num_dislo, Vector<3> &pos0_pbc, Vector<3> &pos0_pbc0,
std::vector<std::vector<Real>> &dislocations, RefAtom &at) {
Vector<3> analytic_Disp = {0.0, 0.0, 0.0};
for (UInt nd = 0; nd < num_dislo; ++nd) {
for (UInt pd = 0; pd < 2; ++pd) {
if (pd == 0) {
// pos0_pbc[0] = pos0_pbc0[0] - dislocations[nd][0];
pos0_pbc[1] = pos0_pbc0[1] - dislocations[nd][1];
pos0_pbc[2] = pos0_pbc0[2] - (dislocations[nd][2] - 1.0);
} else if (pd == 1) {
// pos0_pbc[0] = pos0_pbc0[0] - dislocations[nd][0];
pos0_pbc[1] = pos0_pbc0[1] - dislocations[nd][1];
pos0_pbc[2] = pos0_pbc0[2] - (dislocations[nd][2] + 1.0);
}
Real rotation_matrix[] = {0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0};
Real pos0_pbc_rotate[] = {0.0, 0.0, 0.0};
for (UInt i = 0; i < 3; ++i) {
pos0_pbc_rotate[i] = 0.0;
for (UInt j = 0; j < 3; ++j) {
pos0_pbc_rotate[i] += rotation_matrix[3 * i + j] * pos0_pbc[j];
}
}
LM_TOIMPLEMENT;
// Real b_screw =
// this->burgMag0 * cos(this->angle_between_b_and_x / 180.0 * M_PI)
// / 2.;
// Real b_edge =
// this->burgMag0 * sin(this->angle_between_b_and_x / 180.0 * M_PI)
// / 2.;
// Real analytic_disp[] = {0.0, 0.0, 0.0};
// Real dispV[] = {0.0, 0.0, 0.0};
// analytic_disp[0] = computeBeDisplacement(b_edge, pos0_pbc_rotate);
// analytic_disp[1] = computeNDisplacement(b_edge, pos0_pbc_rotate);
// analytic_disp[2] = computeLDisplacement(b_screw, pos0_pbc_rotate);
// for (UInt i = 0; i < 3; ++i) {
// for (UInt j = 0; j < 3; ++j) {
// dispV[i] += rotation_matrix[3 * j + i] * analytic_disp[j];
// }
// }
// for (UInt i = 0; i < 3; ++i)
// analytic_Disp[i] += dispV[i];
}
}
applyDisplacement(at, analytic_Disp);
}
/* ------------------------------------------------------------------------ */
template <typename Vec, typename RefAtom>
void CADD_template::functionElse(ContainerGenericDDMesh<3> &dd_points
[[gnu::unused]],
Vec &pos0_pbc [[gnu::unused]],
RefAtom &at [[gnu::unused]]) {
// UInt count_pad_core = 0;
// UInt count_far_field = 0;
// UInt count_pad_around = 0;
// Vector<3> barnett_disp = {0.0, 0.0, 0.0};
// Vector<3> dislo_disp = {0.0, 0.0, 0.0};
// Vector<3> temp_disp = {0.0, 0.0, 0.0};
// for (auto &dd_pt : dd_points) {
// // bool selected = false;
// Vector<3> disp_for_segment = {0.0, 0.0, 0.0};
// auto &A = dd_pt.X;
// auto &neigh1 = dd_pt.neighs[0];
// auto &neigh2 = dd_pt.neighs[2];
// auto &pts1 = neigh1.X;
// auto &pts2 = neigh2.X;
// for (UInt j = 0; j < 2; ++j) {
// auto &neigh = dd_pt.neighs[j];
// Vector<3> burg = neigh.burg * this->burgMag0;
// Real bmag = burg[0] * cos(this->angle_between_b_and_x / 180.0 * M_PI)
// +
// burg[2] * sin(this->angle_between_b_and_x / 180.0 *
// M_PI);
// bmag = roundf(bmag * 10.0) / 10.0;
// if (bmag / fabs(bmag) > 0) {
// auto &B = neigh.X;
// auto &normal = neigh.normal;
// UInt slip_case = 3;
// if (fabs(normal[0]) - 1.0 == 0.0)
// slip_case = 0;
// else if (fabs(normal[1]) - 1.0 == 0.0)
// slip_case = 1;
// else if (fabs(normal[2]) - 1.0 == 0.0)
// slip_case = 2;
// else {
// DUMP("[warning] cannot define slip case", DBG_MESSAGE);
// slip_case = 1;
// }
// ComputeAnalyticDisplacements(disp_for_segment, B, A, burg,
// pos0_pbc);
// UInt idx_number = (UInt)neigh.idx;
// auto &pts3 = dd_points[idx_number].tmp_X;
// auto &pts4 = dd_points[idx_number].tmp2_X;
// if (PointInSquare(pts1, pts2, pts3, pts4, pos0_pbc, slip_case)) {
// Real distance = distance_between_line_and_point3D(pos0_pbc, A,
// B); if (distance <= this->core_rcut) {
// bool success(false);
// success = ComputeTemplateDisplacements(temp_disp, A, B, burg,
// normal, pos0_pbc);
// if (!success) {
// barnett_disp += disp_for_segment;
// } else {
// if (distance / this->core_rcut <= this->factor) {
// ++count_pad_core;
// barnett_disp += temp_disp + disp_for_segment;
// } else {
// Real temp_ratio =
// (this->core_rcut - distance) /
// (this->core_rcut - this->core_rcut * this->factor);
// ++count_pad_around;
// barnett_disp += temp_disp * temp_ratio + disp_for_segment;
// }
// }
// } else {
// barnett_disp += disp_for_segment;
// ++count_far_field;
// }
// } else {
// barnett_disp += disp_for_segment;
// ++count_far_field;
// } // if(PointInSquare(pts ... )
// } // if(bmag/abs(bmag)>0)
// } // for (UInt j=0; j < num_neigh; ++j){
// } // for (UInt i=0; i < num_dd_pts;
// dislo_disp = barnett_disp;
// applyDisplacement(at, dislo_disp);
}
/* ------------------------------------------------------------------------ */
DECLARE_COUPLER_INIT_MAKE_CALL(CADD_template, domDD, domMD)
DECLARE_COUPLING_MAKE_CALL(CADD_template, domDD, domMD)
/* ------------------------------------------------------------------------ */
__END_LIBMULTISCALE__

Event Timeline