Hello and welcome the June 2018 desi Traveler Digest, the summer special edition. We have some new travelers joining us while we also welcome back some of our old friends. This month I have carefully chosen images that kind of reminds me of summer holidays that all of us have fond memories and laid the founding stone for our future travels. Mind you these are not typical summer images of scorching heat, but more of celebrating summers as we travel around the world with desi Travelers.
Let us begin by welcoming desi Traveler Pamela Mukherjee, who shares this image from Sydney, down under where this permanent art installation by Artist Michael Thomas Hill takes you back to the forgotten bird songs as the birds were pushed out when Europeans started settling in this part of the city. Based on the time of day you can hear many a bird song in the area that are part of the artwork. During the day, you may hear the calls of the Eastern Whipbird, Rockwarbler, Regent Honeyeater, Grey Shrike-thrush, Fan-tailed Cuckoo, Spotted Pardalote, Brown Gerygone, Jacky Winter, Scarlet Robin and many others. At night, the soundtrack switches to nocturnal birds like the Australian Owlet-nightjar, Powerful Owl, Southern Boobook, Barn Owl, Tawny Frogmouth, and White-throated Nightjar.
Next let us welcome desi Traveler Rupali K, from whose gallery I have chosen 2 beautiful images. The first one is from the daily Arti on the Ghats of Varanasi. But what I like about this image is that while most of us including yours truly typically focus on the choreographed performance, in this image Rupali has taken the image of the pilgrims/spectators as they watch the Arti spellbound.
In the second image by Rupali, she takes us to the small village of Velas in coastal Maharashtra that is a good example of involving local people in conservation efforts of the endangered Olive Ridley Turtles. The village now annually celebrates the birth of next generation of Olive Ridley Turtles, by organizing Velas Turtle Festival the involves locals who open their hearts and homes to visitors from India and abroad. Velas has been on my wishlist for long and it is again one of those destinations that I had to cancel my visit more than once due to some last-minute change of plants, and that only makes my desire to visit Velas even stronger.
Let us have a look at another evening Arti on the Ganga Ghats, but this time in Rishikesh, Uttrakhand, the first town in the Indo-Gangetic plains in the long journey of Ganga. The image clicked by Ravichandra was made on Triveni Ghat in Rishikesh.
Next Vipul Vaibhav takes us to the golden district of India Kolar in Karnataka. Yes, Kolar is the original golden town as it is here the oldest Gold mines are found in India. But this image is about the green Karnataka countryside, with a mythological connection. Check the post below by Vipul to see what is the mythological sagas associated with Avani in Kolar.
If you know me a little, then you know I have a sweet spot for the City of Pearls – Hyderabad and the moment I see an image from Hyderabad I bookmark it of desi Traveler digest. Thanks a lot, Meenakshi J Iyer aka Polka Junction for rekindling the memories from Charminar and the bylanes of the Old City that has a special place in the heart of anybody who has ever been to Hyderabad.
Our first global traveler on this desi Traveler digest is Ashely Nicole who is a true global citizen. Ashley is a Canadian with a dual Great Britain citizenship, but living in France and sharing here images from Montego Bay Jamaica and from Glen Coe Scotland. Both the images are so different from each other yet to me they represent summers in some form.
I also love the quotes she has given with images > "Dear ocean, thank you for making us feel tiny, humble, inspired and salty…all at once." .
I actually feel honored to feature my favorite ( yes I play favorites 😛 ) desi Travel Writer – Anjaly Thomas, who has traveled globally where no travel writer may have dared before. Originally Anjaly is from God's Own Country and she is one of the few travels writers who have visited North Korea where there are no Gods. Yes, that is the title of her best-selling travel book > There are No Gods in North Korea. Go grab a copy if you have not done it already.
Ok, now you may be wondering why I am sharing the image of Shoes on the bank of Danube twice? Well, the second image is by our featured traveler Rohit Jain, who has earlier shared this insightful post about desi Travelers in Bhutan. While in the world of Travel bloggers, those who quit there job and travel are celebrated as heroes, I feel that those who travel with a full-time job are the ones that we need to celebrate and felicitate more. Rohit is an IITian and then did his MBA from IIM Calcutta and serves as CIO in a global IT company and he regularly shuttles between continents, yet he tries to sneak every opportunity to visit the local attractions to the places he visits. I am sharing two sets of images by him and the cover image for this post is clicked by Rohit in Vienna, Austria.
Ok, now the question why I am sharing the image of " Shoes on the Danube Bank" twice? – You see both Anjaly and Rohit were in Budapest and shared this image on their Instagram feed. While they don't know each other, the fact that this image was shared on my timeline made me think about the background of this image, which is an art installation about the Jews, killed by Nazis. Many of these folks were dragged out of their homes and made to line up on the Danube river bank, asked to remove shoes and then were shot so that they fall in the river. This meant there were no graves to be dug and no bodies to be buried. Today we are again slowly moving toward a world where diversity in society is being seen as something bad. Borders are being closed, and anybody who does not subscribe to the view of those in power is an obvious target. This is the time all sane voices need to unite and not let the dark phase of human history repeat itself. You may want to check this very old post from me that I shared on my Instagram long ago.
In the images below Rohit takes us through a fascinating journey to Pompeii, Napoli in Italy. Everything in Pompeii was frozen in time due to the hot lava erupted by Mt. Vesuvius in 79 BC. What an irony it was that hot lave froze everything in a split second not even giving time to think what happened. Check the images clicked by Rohit that take you to the life and times of pre-Christian era Italy as everything was preserved for almost 1900 years before excavation started in 18th century.
With this let me thank each of the travelers who have shared their images with #desiTraveler on Instagram. As we have space constraint I am able to share only a few of the images, but I would urge you to go to Instagram and check the #desiTraveler and see where all desi Travelers have been to. Do you know somebody whom I should feature? Or would you like to be featured on desi Traveler blog? All you need to do is follow and tag @desiTraveler using #desiTraveler on Instagram.
What I like the most about these monthly digest posts is how beautifully you describe each photographer's info and your thoughts on the pictures. It really makes a difference when an expert appreciates your work.


text:
The Communist Manifesto summary
Written by Tom on July 20, 2020 in Politics
Whatever you think of communism, The Communist Manifesto remains one of the most important documents ever written.
It inspired numerous revolutions across the globe and despite being written in 1848 is still relevant today. Indeed, there are many people around the globe who live in Communist states to this day.
Marx and Engels were prophetic about their manifesto, claiming it would be as important for history as Darwin On The Origin of Species, was for biology in the preface to the English edition of the Manifesto in 1888.
While it was not as important as Engels envisioned it to be, it has had a lasting effect on the political discourse.
The ideas in the manifesto by today's standards are extreme and unattainable. The abolition of private property is not going to happen and nor should it.
It's important to remember the context the manifesto was written in, before deriding it. In 1848, a large proportion of the population was destitute and forced to work long hours in horrible conditions.
Placed in that context, it becomes easier to see how the ideas in the book would be attractive to those at the bottom of the ladder.
History has shown us that while the ideas of the manifesto may have sounded good in principle, in practice they led to a dystopia and did not bring about the equality envisioned.
If you're a keen student of history, it's worth reading the manifesto. It will give you a deeper understanding of the politics of Communism and should be read by those on the left and right of the political spectrum.
Takeaway 1 – Theories don't always work in reality
Takeaway 2 – Class struggles define history
Takeaway 3 – Some of the ideas made the cut
The Communist Manifesto review
Who should read The Communist Manifesto?
The manifesto looks at the relationship between the proletariat (the working class) and the bouregoisie (middle classes). The manifesto suggets that the only way for the proletariat to prosper is for them to overthro the bourgeoisie.
The principles of equality sound good in practice. Everyone having a similar house, similar wage and a similar standard of living, these ideas sound fair and reasonable.
The problem is they don't work in reality.
At the time, Marx and Engels were living, democracy was not common. Monarchies were still prevalent and even in countries such as England, only a certain number of the electorate had the vote.
Their ideas would have made sense to them at the time, as the system they saw around them was neither fair nor reasonable.
However, history has proven that their ideas are not feasible in practice. Humans have evolved to live in a society that is envisioned in the manifesto.
You will always find people who want, who are not content with what they have. Competition is a part of life, it's an evolutionary trait that has been handed down by our ancestors.
Countries which adopted Communism such as the Soviet Union, Cuba and North Korea did not bring about fair societies and did not solve the problems of the proletariat.
Theories are great, but their true test comes when they are put into practice. Only the staunchest advocate of Marx will state that Communism has been a success.
One of the main premises of The Communist Manifesto is the following:
"the history of all hitherto existing society is the history of class struggles."
The manifesto argues that societies have always taken the form of an oppressed majority under the yoke of an oppressive minority.
While this may have been the case at the time the manifesto was written, it's hard to say that is the case today in most countries.
Yes, there are people that earn a lot more than the ordinary person, but we have more rights and freedoms than ever before.
Everyone has the ability to ascend to the top if they work hard enough. Under an absolute monarchy, which there were plenty of in 1848, that was an impossibility.
If you were born a peasant, you were likely to remain a peasant. Social mobility was non-existent. It's hard to argue that is the case today.
The irony is that Communism did not resolve this class struggle. The bourgeoisie may have been overthrown, but a new ruling elite replaced them.
In the Soviet Union, this led to a political clique controlling the country, while everyone else had no power. One ruling class was been overthrown to be replaced by another, smaller one.
While inequality is still a major issue and has been getting worse in recent times, you still have much more opportunity than ever before.
Class struggle may become a theme again though, despite this. Brexit and the election of Trump show voters are willing to upend the established view if they so desire.
Maybe the era of class struggle is not over.
While Communism failed as an ideology, some of the suggestions in the manifesto did make the cut.
It's easy to dismiss the manifesto, due to its relation to the ideology of Communism, but in some areas it has validity.
The second section entitled, "Proletarians and Communists", has some demands which became law and are accepted by all sides of the political spectrum today.
The main one is the abolition of child labour. When the manifesto was written, it was rampant, yet today in the western world, it has been eradicated.
Children are now sent to school to learn and no one is advocating for a return to children joining the workforce and nor, should they!
Likewise, most countries have a progressive income tax, even if there are people on the right who want to lower taxes substantially. Again, this is accepted as a staple of democratic countries.
While free education is commonplace in most countries, it is not the case everywhere. However, most people would agree that it is not an outrageous demand.
Nationalisation of transport and the means of communication is a contentious issue, but by no mean an outrageous one. It works in a lot of countries.
However, the abolition of inheritance and private property is not a feasible demand for the 21st century.
What this shows is that despite the association of poverty and debunked ideas that goes along with the manifesto, a deeper reading shows that there are some aspects which are relevant.
It's easy to criticise the manifesto, because of the connotations of Communism, but unless you've read it and actually look at some of the things that were advocated, that criticism is not valid.
You can find aspects of any ideology that make sense and have merit. It's easy to deride things as black and white, but the world is a lot greyer than we like to admit.
In a world that is becoming increasingly polarised, it's important to take a step back and have a balanced view of matters. Nor one that is clouded by preconceptions and bias.
"The whole history of mankind (since the dissolution of primitive tribal society, holding land in common ownership) has been a history of class struggles, contests between exploiting and exploited, ruling and oppressed classes…"
"The working men have no country. We cannot take from them what they have not got."
"It has been objected that upon the abolition of private property all work will cease, and universal laziness will overtake us."
"The average price of wage labour is the minimum wage i.e. that quantum of the means of subsistence which is absolutely requisite to keep the labourer in bare existence as a labourer."
"…the theory of the Communists may be summed up in the single sentence: Abolition of private property."
"The bourgeoisie cannot exist without constantly revolutionising the instruments of production, and thereby the relations of production, and with them the whole relations of society."
This The Communist Manifesto summary has looked at one of the most influential political texts in . history. This text shaped world events for decades.
While the ideology has shown to be completely debunked by events over the past century, it's still an important text that should be read.
The label of Marxist and far left gets thrown around a lot today. but the people doing this have likely never read the manifesto from which these insults are derived.
Even if you don't agree with the ideology, it's important to learn more about it as you can offer a more informed opinion. Dismissing the manifesto is easy, reading it when you disagree with something is much harder.
As I said above, some of the points in the manifesto have become standard practice today. It was the wider concept of society that Marx and Engels got wrong.
But it's important to remember the manifesto was originally written in 1848. Nearly two hundred years ago! The world is a different place now.
Unlike Meditations or Letters From a Stoic, the manifesto has not stood the test of time and isn't a viable ideology today.
Anyone interested in politics or history should read The Communist Manifesto. It's such an important document that you have to read it.
I still can't believe I'd never read it during all my time studying history at university and school!
Those who want to get a better understanding of Marxism, should its foundational document and then supplement with Lenin's Imperialism: The Highest Stage of Capitalism.
On Living and Dying Well summary
Meditations Summary

redpajama_set_name: RedPajamaCommonCrawl


text:
//Code written by Richard O. Lee and Christian Bienia
//Modified by Christian Fensch

#include <cstdlib>
#include <cstring>

#include <iostream>
#include <fstream>
#if defined(WIN32)
#define NOMINMAX
#include <windows.h>
#endif
#include <math.h>
#include <pthread.h>
#include <assert.h>
#include <float.h>

#include "fluid.hpp"
#include "cellpool.hpp"
#include "parsec_barrier.hpp"

#ifdef ENABLE_VISUALIZATION
#include "fluidview.hpp"
#endif

#ifdef ENABLE_PARSEC_HOOKS
#include <hooks.h>
#endif

//Uncomment to add code to check that Courant–Friedrichs–Lewy condition is satisfied at runtime
//#define ENABLE_CFL_CHECK

////////////////////////////////////////////////////////////////////////////////

cellpool *pools; //each thread has its private cell pool

fptype restParticlesPerMeter, h, hSq;
fptype densityCoeff, pressureCoeff, viscosityCoeff;

int nx, ny, nz;    // number of grid cells in each dimension
Vec3 delta;        // cell dimensions
int numParticles = 0;
int numCells = 0;
Cell *cells = 0;
Cell *cells2 = 0;
int *cnumPars = 0;
int *cnumPars2 = 0;
Cell **last_cells = NULL; //helper array with pointers to last cell structure of "cells" array lists
#ifdef ENABLE_VISUALIZATION
Vec3 vMax(0.0,0.0,0.0);
Vec3 vMin(0.0,0.0,0.0);
#endif

int XDIVS = 1;  // number of partitions in X
int ZDIVS = 1;  // number of partitions in Z

#define NUM_GRIDS  ((XDIVS) * (ZDIVS))
#define MUTEXES_PER_CELL 128
#define CELL_MUTEX_ID 0

struct Grid
{
  union {
    struct {
      int sx, sy, sz;
      int ex, ey, ez;
    };
    unsigned char pp[CACHELINE_SIZE];
  };
} *grids;
bool  *border;
pthread_attr_t attr;
pthread_t *thread;
pthread_mutex_t **mutex;  // used to lock cells in RebuildGrid and also particles in other functions
pthread_barrier_t barrier;  // global barrier used by all threads
#ifdef ENABLE_VISUALIZATION
pthread_barrier_t visualization_barrier;  // global barrier to separate (serial) visualization phase from (parallel) fluid simulation
#endif

typedef struct __thread_args {
  int tid;      //thread id, determines work partition
  int frames;      //number of frames to compute
} thread_args;      //arguments for threads

////////////////////////////////////////////////////////////////////////////////

/*
 * hmgweight
 *
 * Computes the hamming weight of x
 *
 * x      - input value
 * lsb    - if x!=0 position of smallest bit set, else -1
 *
 * return - the hamming weight
 */
unsigned int hmgweight(unsigned int x, int *lsb) {
  unsigned int weight=0;
  unsigned int mask= 1;
  unsigned int count=0;

  *lsb=-1;
  while(x > 0) {
    unsigned int temp;
    temp=(x&mask);
    if((x&mask) == 1) {
      weight++;
      if(*lsb == -1) *lsb = count;
    }
    x >>= 1;
    count++;
  }

  return weight;
}

void InitSim(char const *fileName, unsigned int threadnum)
{
  //Compute partitioning based on square root of number of threads
  //NOTE: Other partition sizes are possible as long as XDIVS * ZDIVS == threadnum,
  //      but communication is minimal (and hence optimal) if XDIVS == ZDIVS
  int lsb;
  if(hmgweight(threadnum,&lsb) != 1) {
    std::cerr << "Number of threads must be a power of 2" << std::endl;
    exit(1);
  }
  XDIVS = 1<<(lsb/2);
  ZDIVS = 1<<(lsb/2);
  if(XDIVS*ZDIVS != threadnum) XDIVS*=2;
  assert(XDIVS * ZDIVS == threadnum);

  thread = new pthread_t[NUM_GRIDS];
  grids = new struct Grid[NUM_GRIDS];
  assert(sizeof(Grid) <= CACHELINE_SIZE); // as we put and aligh grid on the cacheline size to avoid false-sharing
                                          // if asserts fails - increase pp union member in Grid declarationi
                                          // and change this macro
  pools = new cellpool[NUM_GRIDS];

  //Load input particles
  std::cout << "Loading file \"" << fileName << "\"..." << std::endl;
  std::ifstream file(fileName, std::ios::binary);
  if(!file) {
    std::cerr << "Error opening file. Aborting." << std::endl;
    exit(1);
  }

  //Always use single precision float variables b/c file format uses single precision
  float restParticlesPerMeter_le;
  int numParticles_le;
  file.read((char *)&restParticlesPerMeter_le, FILE_SIZE_FLOAT);
  file.read((char *)&numParticles_le, FILE_SIZE_INT);
  if(!isLittleEndian()) {
    restParticlesPerMeter = bswap_float(restParticlesPerMeter_le);
    numParticles          = bswap_int32(numParticles_le);
  } else {
    restParticlesPerMeter = restParticlesPerMeter_le;
    numParticles          = numParticles_le;
  }
  for(int i=0; i<NUM_GRIDS; i++) cellpool_init(&pools[i], numParticles/NUM_GRIDS);

  h = kernelRadiusMultiplier / restParticlesPerMeter;
  hSq = h*h;

#ifndef ENABLE_DOUBLE_PRECISION
  fptype coeff1 = 315.0 / (64.0*pi*powf(h,9.0));
  fptype coeff2 = 15.0 / (pi*powf(h,6.0));
  fptype coeff3 = 45.0 / (pi*powf(h,6.0));
#else
  fptype coeff1 = 315.0 / (64.0*pi*pow(h,9.0));
  fptype coeff2 = 15.0 / (pi*pow(h,6.0));
  fptype coeff3 = 45.0 / (pi*pow(h,6.0));
#endif //ENABLE_DOUBLE_PRECISION
  fptype particleMass = 0.5*doubleRestDensity / (restParticlesPerMeter*restParticlesPerMeter*restParticlesPerMeter);
  densityCoeff = particleMass * coeff1;
  pressureCoeff = 3.0*coeff2 * 0.50*stiffnessPressure * particleMass;
  viscosityCoeff = viscosity * coeff3 * particleMass;

  Vec3 range = domainMax - domainMin;
  nx = (int)(range.x / h);
  ny = (int)(range.y / h);
  nz = (int)(range.z / h);
  assert(nx >= 1 && ny >= 1 && nz >= 1);
  numCells = nx*ny*nz;
  std::cout << "Number of cells: " << numCells << std::endl;
  delta.x = range.x / nx;
  delta.y = range.y / ny;
  delta.z = range.z / nz;
  assert(delta.x >= h && delta.y >= h && delta.z >= h);

  std::cout << "Grids steps over x, y, z: " << delta.x << " " << delta.y << " " << delta.z << std::endl;

  assert(nx >= XDIVS && nz >= ZDIVS);
  int gi = 0;
  int sx, sz, ex, ez;
  ex = 0;
  for(int i = 0; i < XDIVS; ++i)
  {
    sx = ex;
    ex = (int)((fptype)(nx)/(fptype)(XDIVS) * (i+1) + 0.5);
    assert(sx < ex);

    ez = 0;
    for(int j = 0; j < ZDIVS; ++j, ++gi)
    {
      sz = ez;
      ez = (int)((fptype)(nz)/(fptype)(ZDIVS) * (j+1) + 0.5);
      assert(sz < ez);

      grids[gi].sx = sx;
      grids[gi].ex = ex;
      grids[gi].sy = 0;
      grids[gi].ey = ny;
      grids[gi].sz = sz;
      grids[gi].ez = ez;
    }
  }
  assert(gi == NUM_GRIDS);

  border = new bool[numCells];
  for(int i = 0; i < NUM_GRIDS; ++i)
    for(int iz = grids[i].sz; iz < grids[i].ez; ++iz)
      for(int iy = grids[i].sy; iy < grids[i].ey; ++iy)
        for(int ix = grids[i].sx; ix < grids[i].ex; ++ix)
        {
          int index = (iz*ny + iy)*nx + ix;
          border[index] = false;
          for(int dk = -1; dk <= 1; ++dk)
	  {
            for(int dj = -1; dj <= 1; ++dj)
	    {
              for(int di = -1; di <= 1; ++di)
              {
                int ci = ix + di;
                int cj = iy + dj;
                int ck = iz + dk;

                if(ci < 0) ci = 0; else if(ci > (nx-1)) ci = nx-1;
                if(cj < 0) cj = 0; else if(cj > (ny-1)) cj = ny-1;
                if(ck < 0) ck = 0; else if(ck > (nz-1)) ck = nz-1;

                if( ci < grids[i].sx || ci >= grids[i].ex ||
                  cj < grids[i].sy || cj >= grids[i].ey ||
                  ck < grids[i].sz || ck >= grids[i].ez ) {

                    border[index] = true;
		    break;
		}
              } // for(int di = -1; di <= 1; ++di)
	      if(border[index])
		break;
	    } // for(int dj = -1; dj <= 1; ++dj)
	    if(border[index])
	       break;
           } // for(int dk = -1; dk <= 1; ++dk)
        }

  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

  mutex = new pthread_mutex_t *[numCells];
  for(int i = 0; i < numCells; ++i)
  {
    assert(CELL_MUTEX_ID < MUTEXES_PER_CELL);
    int n = (border[i] ? MUTEXES_PER_CELL : CELL_MUTEX_ID+1);
    mutex[i] = new pthread_mutex_t[n];
    for(int j = 0; j < n; ++j)
      pthread_mutex_init(&mutex[i][j], NULL);
  }
  pthread_barrier_init(&barrier, NULL, NUM_GRIDS);
#ifdef ENABLE_VISUALIZATION
  //visualization barrier is used by all NUM_GRIDS worker threads and 1 master thread
  pthread_barrier_init(&visualization_barrier, NULL, NUM_GRIDS+1);
#endif
  //make sure Cell structure is multiple of estiamted cache line size
  assert(sizeof(Cell) % CACHELINE_SIZE == 0);
  //make sure helper Cell structure is in sync with real Cell structure
  assert(offsetof(struct Cell_aux, padding) == offsetof(struct Cell, padding));

#if defined(WIN32)
  cells = (struct Cell*)_aligned_malloc(sizeof(struct Cell) * numCells, CACHELINE_SIZE);
  cells2 = (struct Cell*)_aligned_malloc(sizeof(struct Cell) * numCells, CACHELINE_SIZE);
  cnumPars = (int*)_aligned_malloc(sizeof(int) * numCells, CACHELINE_SIZE);
  cnumPars2 = (int*)_aligned_malloc(sizeof(int) * numCells, CACHELINE_SIZE);
  last_cells = (struct Cell **)_aligned_malloc(sizeof(struct Cell *) * numCells, CACHELINE_SIZE);
  assert((cells!=NULL) && (cells2!=NULL) && (cnumPars!=NULL) && (cnumPars2!=NULL) && (last_cells!=NULL));
#elif defined(SPARC_SOLARIS)
  cells = (Cell*)memalign(CACHELINE_SIZE, sizeof(struct Cell) * numCells);
  cells2 =  (Cell*)memalign(CACHELINE_SIZE, sizeof(struct Cell) * numCells);
  cnumPars =  (int*)memalign(CACHELINE_SIZE, sizeof(int) * numCells);
  cnumPars2 =  (int*)memalign(CACHELINE_SIZE, sizeof(int) * numCells);
  last_cells =  (Cell**)memalign(CACHELINE_SIZE, sizeof(struct Cell *) * numCells);
  assert((cells!=0) && (cells2!=0) && (cnumPars!=0) && (cnumPars2!=0) && (last_cells!=0));
#else
  int rv0 = posix_memalign((void **)(&cells), CACHELINE_SIZE, sizeof(struct Cell) * numCells);
  int rv1 = posix_memalign((void **)(&cells2), CACHELINE_SIZE, sizeof(struct Cell) * numCells);
  int rv2 = posix_memalign((void **)(&cnumPars), CACHELINE_SIZE, sizeof(int) * numCells);
  int rv3 = posix_memalign((void **)(&cnumPars2), CACHELINE_SIZE, sizeof(int) * numCells);
  int rv4 = posix_memalign((void **)(&last_cells), CACHELINE_SIZE, sizeof(struct Cell *) * numCells);
  assert((rv0==0) && (rv1==0) && (rv2==0) && (rv3==0) && (rv4==0));
#endif

  // because cells and cells2 are not allocated via new
  // we construct them here
  for(int i=0; i<numCells; ++i)
  {
	  new (&cells[i]) Cell;
	  new (&cells2[i]) Cell;
  }

  memset(cnumPars, 0, numCells*sizeof(int));

  //Always use single precision float variables b/c file format uses single precision float
  int pool_id = 0;
  float px, py, pz, hvx, hvy, hvz, vx, vy, vz;
  for(int i = 0; i < numParticles; ++i)
  {
    file.read((char *)&px, FILE_SIZE_FLOAT);
    file.read((char *)&py, FILE_SIZE_FLOAT);
    file.read((char *)&pz, FILE_SIZE_FLOAT);
    file.read((char *)&hvx, FILE_SIZE_FLOAT);
    file.read((char *)&hvy, FILE_SIZE_FLOAT);
    file.read((char *)&hvz, FILE_SIZE_FLOAT);
    file.read((char *)&vx, FILE_SIZE_FLOAT);
    file.read((char *)&vy, FILE_SIZE_FLOAT);
    file.read((char *)&vz, FILE_SIZE_FLOAT);
    if(!isLittleEndian()) {
      px  = bswap_float(px);
      py  = bswap_float(py);
      pz  = bswap_float(pz);
      hvx = bswap_float(hvx);
      hvy = bswap_float(hvy);
      hvz = bswap_float(hvz);
      vx  = bswap_float(vx);
      vy  = bswap_float(vy);
      vz  = bswap_float(vz);
    }

    int ci = (int)((px - domainMin.x) / delta.x);
    int cj = (int)((py - domainMin.y) / delta.y);
    int ck = (int)((pz - domainMin.z) / delta.z);

    if(ci < 0) ci = 0; else if(ci > (nx-1)) ci = nx-1;
    if(cj < 0) cj = 0; else if(cj > (ny-1)) cj = ny-1;
    if(ck < 0) ck = 0; else if(ck > (nz-1)) ck = nz-1;

    int index = (ck*ny + cj)*nx + ci;
    Cell *cell = &cells[index];

    //go to last cell structure in list
    int np = cnumPars[index];
    while(np > PARTICLES_PER_CELL) {
      cell = cell->next;
      np = np - PARTICLES_PER_CELL;
    }
    //add another cell structure if everything full
    if( (np % PARTICLES_PER_CELL == 0) && (cnumPars[index] != 0) ) {
      //Get cells from pools in round-robin fashion to balance load during parallel phase
      cell->next = cellpool_getcell(&pools[pool_id]);
      pool_id = (pool_id+1) % NUM_GRIDS;
      cell = cell->next;
      np = np - PARTICLES_PER_CELL;
    }

    cell->p[np].x = px;
    cell->p[np].y = py;
    cell->p[np].z = pz;
    cell->hv[np].x = hvx;
    cell->hv[np].y = hvy;
    cell->hv[np].z = hvz;
    cell->v[np].x = vx;
    cell->v[np].y = vy;
    cell->v[np].z = vz;
#ifdef ENABLE_VISUALIZATION
	vMin.x = std::min(vMin.x, cell->v[np].x);
	vMax.x = std::max(vMax.x, cell->v[np].x);
	vMin.y = std::min(vMin.y, cell->v[np].y);
	vMax.y = std::max(vMax.y, cell->v[np].y);
	vMin.z = std::min(vMin.z, cell->v[np].z);
	vMax.z = std::max(vMax.z, cell->v[np].z);
#endif
    ++cnumPars[index];
  }

  std::cout << "Number of particles: " << numParticles << std::endl;
}

////////////////////////////////////////////////////////////////////////////////

void SaveFile(char const *fileName)
{
  std::cout << "Saving file \"" << fileName << "\"..." << std::endl;

  std::ofstream file(fileName, std::ios::binary);
  assert(file);

  //Always use single precision float variables b/c file format uses single precision
  if(!isLittleEndian()) {
    float restParticlesPerMeter_le;
    int   numParticles_le;

    restParticlesPerMeter_le = bswap_float((float)restParticlesPerMeter);
    numParticles_le      = bswap_int32(numParticles);
    file.write((char *)&restParticlesPerMeter_le, FILE_SIZE_FLOAT);
    file.write((char *)&numParticles_le,      FILE_SIZE_INT);
  } else {
    file.write((char *)&restParticlesPerMeter, FILE_SIZE_FLOAT);
    file.write((char *)&numParticles,      FILE_SIZE_INT);
  }

  int count = 0;
  for(int i = 0; i < numCells; ++i)
  {
    Cell *cell = &cells[i];
    int np = cnumPars[i];
    for(int j = 0; j < np; ++j)
    {
      //Always use single precision float variables b/c file format uses single precision
      float px, py, pz, hvx, hvy, hvz, vx,vy, vz;
      if(!isLittleEndian()) {
        px  = bswap_float((float)(cell->p[j % PARTICLES_PER_CELL].x));
        py  = bswap_float((float)(cell->p[j % PARTICLES_PER_CELL].y));
        pz  = bswap_float((float)(cell->p[j % PARTICLES_PER_CELL].z));
        hvx = bswap_float((float)(cell->hv[j % PARTICLES_PER_CELL].x));
        hvy = bswap_float((float)(cell->hv[j % PARTICLES_PER_CELL].y));
        hvz = bswap_float((float)(cell->hv[j % PARTICLES_PER_CELL].z));
        vx  = bswap_float((float)(cell->v[j % PARTICLES_PER_CELL].x));
        vy  = bswap_float((float)(cell->v[j % PARTICLES_PER_CELL].y));
        vz  = bswap_float((float)(cell->v[j % PARTICLES_PER_CELL].z));
      } else {
        px  = (float)(cell->p[j % PARTICLES_PER_CELL].x);
        py  = (float)(cell->p[j % PARTICLES_PER_CELL].y);
        pz  = (float)(cell->p[j % PARTICLES_PER_CELL].z);
        hvx = (float)(cell->hv[j % PARTICLES_PER_CELL].x);
        hvy = (float)(cell->hv[j % PARTICLES_PER_CELL].y);
        hvz = (float)(cell->hv[j % PARTICLES_PER_CELL].z);
        vx  = (float)(cell->v[j % PARTICLES_PER_CELL].x);
        vy  = (float)(cell->v[j % PARTICLES_PER_CELL].y);
        vz  = (float)(cell->v[j % PARTICLES_PER_CELL].z);
      }
      file.write((char *)&px,  FILE_SIZE_FLOAT);
      file.write((char *)&py,  FILE_SIZE_FLOAT);
      file.write((char *)&pz,  FILE_SIZE_FLOAT);
      file.write((char *)&hvx, FILE_SIZE_FLOAT);
      file.write((char *)&hvy, FILE_SIZE_FLOAT);
      file.write((char *)&hvz, FILE_SIZE_FLOAT);
      file.write((char *)&vx,  FILE_SIZE_FLOAT);
      file.write((char *)&vy,  FILE_SIZE_FLOAT);
      file.write((char *)&vz,  FILE_SIZE_FLOAT);
      ++count;

      //move pointer to next cell in list if end of array is reached
      if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
        cell = cell->next;
      }

    }
  }
  assert(count == numParticles);
}

////////////////////////////////////////////////////////////////////////////////

void CleanUpSim()
{
  // first return extended cells to cell pools
  for(int i=0; i< numCells; ++i)
  {
    Cell& cell = cells[i];
	while(cell.next)
	{
		Cell *temp = cell.next;
		cell.next = temp->next;
		cellpool_returncell(&pools[0], temp);
	}
  }
  // now return cell pools
  //NOTE: Cells from cell pools can migrate to different pools during the parallel phase.
  //      This is no problem as long as all cell pools are destroyed together. Each pool
  //      uses its internal meta information to free exactly the cells which it allocated
  //      itself. This guarantees that all allocated cells will be freed but it might
  //      render other cell pools unusable so they also have to be destroyed.
  for(int i=0; i<NUM_GRIDS; i++) cellpool_destroy(&pools[i]);
  pthread_attr_destroy(&attr);

  for(int i = 0; i < numCells; ++i)
  {
    assert(CELL_MUTEX_ID < MUTEXES_PER_CELL);
    int n = (border[i] ? MUTEXES_PER_CELL : CELL_MUTEX_ID+1);
    for(int j = 0; j < n; ++j)
      pthread_mutex_destroy(&mutex[i][j]);
    delete[] mutex[i];
  }
  delete[] mutex;
  pthread_barrier_destroy(&barrier);
#ifdef ENABLE_VISUALIZATION
  pthread_barrier_destroy(&visualization_barrier);
#endif

  delete[] border;

#if defined(WIN32)
  _aligned_free(cells);
  _aligned_free(cells2);
  _aligned_free(cnumPars);
  _aligned_free(cnumPars2);
  _aligned_free(last_cells);
#else
  free(cells);
  free(cells2);
  free(cnumPars);
  free(cnumPars2);
  free(last_cells);
#endif
  delete[] thread;
  delete[] grids;
}

////////////////////////////////////////////////////////////////////////////////

void ClearParticlesMT(int tid)
{
  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
        int index = (iz*ny + iy)*nx + ix;
        cnumPars[index] = 0;
		cells[index].next = NULL;
        last_cells[index] = &cells[index];
      }
}

////////////////////////////////////////////////////////////////////////////////

void RebuildGridMT(int tid)
{
  // Note, in parallel versions the below swaps
  // occure outside RebuildGrid()
  // swap src and dest arrays with particles
  //   std::swap(cells, cells2);
  // swap src and dest arrays with counts of particles
  //  std::swap(cnumPars, cnumPars2);

  //iterate through source cell lists
  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
        int index2 = (iz*ny + iy)*nx + ix;
        Cell *cell2 = &cells2[index2];
        int np2 = cnumPars2[index2];
        //iterate through source particles
        for(int j = 0; j < np2; ++j)
        {
          //get destination for source particle
          int ci = (int)((cell2->p[j % PARTICLES_PER_CELL].x - domainMin.x) / delta.x);
          int cj = (int)((cell2->p[j % PARTICLES_PER_CELL].y - domainMin.y) / delta.y);
          int ck = (int)((cell2->p[j % PARTICLES_PER_CELL].z - domainMin.z) / delta.z);

          if(ci < 0) ci = 0; else if(ci > (nx-1)) ci = nx-1;
          if(cj < 0) cj = 0; else if(cj > (ny-1)) cj = ny-1;
          if(ck < 0) ck = 0; else if(ck > (nz-1)) ck = nz-1;
#if 0
		  assert(ci>=ix-1);
		  assert(ci<=ix+1);
		  assert(cj>=iy-1);
		  assert(cj<=iy+1);
		  assert(ck>=iz-1);
		  assert(ck<=iz+1);
#endif
#ifdef ENABLE_CFL_CHECK
          //check that source cell is a neighbor of destination cell
          bool cfl_cond_satisfied=false;
          for(int di = -1; di <= 1; ++di)
            for(int dj = -1; dj <= 1; ++dj)
              for(int dk = -1; dk <= 1; ++dk)
              {
                int ii = ci + di;
                int jj = cj + dj;
                int kk = ck + dk;
                if(ii >= 0 && ii < nx && jj >= 0 && jj < ny && kk >= 0 && kk < nz)
                {
                  int index = (kk*ny + jj)*nx + ii;
                  if(index == index2)
                  {
                    cfl_cond_satisfied=true;
                    break;
                  }
                }
              }
          if(!cfl_cond_satisfied)
          {
            std::cerr << "FATAL ERROR: Courant–Friedrichs–Lewy condition not satisfied." << std::endl;
            exit(1);
          }
#endif //ENABLE_CFL_CHECK

          int index = (ck*ny + cj)*nx + ci;
          // this assumes that particles cannot travel more than one grid cell per time step
          if(border[index])
            pthread_mutex_lock(&mutex[index][CELL_MUTEX_ID]);
          Cell *cell = last_cells[index];
          int np = cnumPars[index];

          //add another cell structure if everything full
          if( (np % PARTICLES_PER_CELL == 0) && (cnumPars[index] != 0) ) {
            cell->next = cellpool_getcell(&pools[tid]);
            cell = cell->next;
            last_cells[index] = cell;
          }
          ++cnumPars[index];
          if(border[index])
            pthread_mutex_unlock(&mutex[index][CELL_MUTEX_ID]);

          //copy source to destination particle

          cell->p[np % PARTICLES_PER_CELL]  = cell2->p[j % PARTICLES_PER_CELL];
          cell->hv[np % PARTICLES_PER_CELL] = cell2->hv[j % PARTICLES_PER_CELL];
          cell->v[np % PARTICLES_PER_CELL]  = cell2->v[j % PARTICLES_PER_CELL];
          //move pointer to next source cell in list if end of array is reached
          if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
            Cell *temp = cell2;
            cell2 = cell2->next;
            //return cells to pool that are not statically allocated head of lists
            if(temp != &cells2[index2]) {
              //NOTE: This is thread-safe because temp and pool are thread-private, no need to synchronize
              cellpool_returncell(&pools[tid], temp);
            }
          }
        } // for(int j = 0; j < np2; ++j)
        //return cells to pool that are not statically allocated head of lists
        if((cell2 != NULL) && (cell2 != &cells2[index2])) {
          cellpool_returncell(&pools[tid], cell2);
    }
      }
}

////////////////////////////////////////////////////////////////////////////////

int InitNeighCellList(int ci, int cj, int ck, int *neighCells)
{
  int numNeighCells = 0;

  // have the nearest particles first -> help branch prediction
  int my_index = (ck*ny + cj)*nx + ci;
  neighCells[numNeighCells] = my_index;
  ++numNeighCells;

  for(int di = -1; di <= 1; ++di)
    for(int dj = -1; dj <= 1; ++dj)
      for(int dk = -1; dk <= 1; ++dk)
      {
        int ii = ci + di;
        int jj = cj + dj;
        int kk = ck + dk;
        if(ii >= 0 && ii < nx && jj >= 0 && jj < ny && kk >= 0 && kk < nz)
        {
          int index = (kk*ny + jj)*nx + ii;
          if((index < my_index) && (cnumPars[index] != 0))
          {
            neighCells[numNeighCells] = index;
            ++numNeighCells;
          }
        }
      }
  return numNeighCells;
}

////////////////////////////////////////////////////////////////////////////////

void InitDensitiesAndForcesMT(int tid)
{
  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
                    int index = (iz*ny + iy)*nx + ix;
        Cell *cell = &cells[index];
        int np = cnumPars[index];
        for(int j = 0; j < np; ++j)
        {
          cell->density[j % PARTICLES_PER_CELL] = 0.0;
          cell->a[j % PARTICLES_PER_CELL] = externalAcceleration;
          //move pointer to next cell in list if end of array is reached
          if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
            cell = cell->next;
          }
        }
      }
}

////////////////////////////////////////////////////////////////////////////////

void ComputeDensitiesMT(int tid)
{
  int neighCells[3*3*3];

  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
        int index = (iz*ny + iy)*nx + ix;
        int np = cnumPars[index];
        if(np == 0)
          continue;

        int numNeighCells = InitNeighCellList(ix, iy, iz, neighCells);

        Cell *cell = &cells[index];
        for(int ipar = 0; ipar < np; ++ipar)
        {
          for(int inc = 0; inc < numNeighCells; ++inc)
          {
            int indexNeigh = neighCells[inc];
            Cell *neigh = &cells[indexNeigh];
            int numNeighPars = cnumPars[indexNeigh];
            for(int iparNeigh = 0; iparNeigh < numNeighPars; ++iparNeigh)
            {
              //Check address to make sure densities are computed only once per pair
              if(&neigh->p[iparNeigh % PARTICLES_PER_CELL] < &cell->p[ipar % PARTICLES_PER_CELL])
              {
                fptype distSq = (cell->p[ipar % PARTICLES_PER_CELL] - neigh->p[iparNeigh % PARTICLES_PER_CELL]).GetLengthSq();
                if(distSq < hSq)
                {
                  fptype t = hSq - distSq;
                  fptype tc = t*t*t;

                  if(border[index])
                  {
                    pthread_mutex_lock(&mutex[index][ipar % MUTEXES_PER_CELL]);
                    cell->density[ipar % PARTICLES_PER_CELL] += tc;
                    pthread_mutex_unlock(&mutex[index][ipar % MUTEXES_PER_CELL]);
                  }
                  else
                    cell->density[ipar % PARTICLES_PER_CELL] += tc;

                  if(border[indexNeigh])
                  {
                    pthread_mutex_lock(&mutex[indexNeigh][iparNeigh % MUTEXES_PER_CELL]);
                    neigh->density[iparNeigh % PARTICLES_PER_CELL] += tc;
                    pthread_mutex_unlock(&mutex[indexNeigh][iparNeigh % MUTEXES_PER_CELL]);
                  }
                  else
                    neigh->density[iparNeigh % PARTICLES_PER_CELL] += tc;
                }
              }
              //move pointer to next cell in list if end of array is reached
              if(iparNeigh % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
                neigh = neigh->next;
              }
            }
          }
          //move pointer to next cell in list if end of array is reached
          if(ipar % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
            cell = cell->next;
          }
        }
      }
}

////////////////////////////////////////////////////////////////////////////////

void ComputeDensities2MT(int tid)
{
  const fptype tc = hSq*hSq*hSq;
  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
        int index = (iz*ny + iy)*nx + ix;
        Cell *cell = &cells[index];
        int np = cnumPars[index];
        for(int j = 0; j < np; ++j)
        {
          cell->density[j % PARTICLES_PER_CELL] += tc;
          cell->density[j % PARTICLES_PER_CELL] *= densityCoeff;
          //move pointer to next cell in list if end of array is reached
          if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
            cell = cell->next;
          }
        }
      }
}

////////////////////////////////////////////////////////////////////////////////

void ComputeForcesMT(int tid)
{
  int neighCells[3*3*3];

  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
        int index = (iz*ny + iy)*nx + ix;
        int np = cnumPars[index];
        if(np == 0)
          continue;

        int numNeighCells = InitNeighCellList(ix, iy, iz, neighCells);

        Cell *cell = &cells[index];
        for(int ipar = 0; ipar < np; ++ipar)
        {
          for(int inc = 0; inc < numNeighCells; ++inc)
          {
            int indexNeigh = neighCells[inc];
            Cell *neigh = &cells[indexNeigh];
            int numNeighPars = cnumPars[indexNeigh];
            for(int iparNeigh = 0; iparNeigh < numNeighPars; ++iparNeigh)
            {
              //Check address to make sure forces are computed only once per pair
              if(&neigh->p[iparNeigh % PARTICLES_PER_CELL] < &cell->p[ipar % PARTICLES_PER_CELL])
              {
                Vec3 disp = cell->p[ipar % PARTICLES_PER_CELL] - neigh->p[iparNeigh % PARTICLES_PER_CELL];
                fptype distSq = disp.GetLengthSq();
                if(distSq < hSq)
                {
#ifndef ENABLE_DOUBLE_PRECISION
                  fptype dist = sqrtf(std::max(distSq, (fptype)1e-12));
#else
                  fptype dist = sqrt(std::max(distSq, 1e-12));
#endif //ENABLE_DOUBLE_PRECISION
                  fptype hmr = h - dist;

                  Vec3 acc = disp * pressureCoeff * (hmr*hmr/dist) * (cell->density[ipar % PARTICLES_PER_CELL]+neigh->density[iparNeigh % PARTICLES_PER_CELL] - doubleRestDensity);
                  acc += (neigh->v[iparNeigh % PARTICLES_PER_CELL] - cell->v[ipar % PARTICLES_PER_CELL]) * viscosityCoeff * hmr;
                  acc /= cell->density[ipar % PARTICLES_PER_CELL] * neigh->density[iparNeigh % PARTICLES_PER_CELL];

                  if( border[index])
                  {
                    pthread_mutex_lock(&mutex[index][ipar % MUTEXES_PER_CELL]);
                    cell->a[ipar % PARTICLES_PER_CELL] += acc;
                    pthread_mutex_unlock(&mutex[index][ipar % MUTEXES_PER_CELL]);
                  }
                  else
                    cell->a[ipar % PARTICLES_PER_CELL] += acc;

                  if( border[indexNeigh])
                  {
                    pthread_mutex_lock(&mutex[indexNeigh][iparNeigh % MUTEXES_PER_CELL]);
                    neigh->a[iparNeigh % PARTICLES_PER_CELL] -= acc;
                    pthread_mutex_unlock(&mutex[indexNeigh][iparNeigh % MUTEXES_PER_CELL]);
                  }
                  else
                    neigh->a[iparNeigh % PARTICLES_PER_CELL] -= acc;
                }
              }
              //move pointer to next cell in list if end of array is reached
              if(iparNeigh % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
                neigh = neigh->next;
              }
            }
          }
          //move pointer to next cell in list if end of array is reached
          if(ipar % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
            cell = cell->next;
          }
        }
      }
}

////////////////////////////////////////////////////////////////////////////////

// ProcessCollisions() with container walls
// Under the assumptions that
// a) a particle will not penetrate a wall
// b) a particle will not migrate further than once cell
// c) the parSize is smaller than a cell
// then only the particles at the perimiters may be influenced by the walls
#if 0
void ProcessCollisionsMT(int tid)
{
  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
        int index = (iz*ny + iy)*nx + ix;
        Cell *cell = &cells[index];
        int np = cnumPars[index];
        for(int j = 0; j < np; ++j)
        {
          Vec3 pos = cell->p[j % PARTICLES_PER_CELL] + cell->hv[j % PARTICLES_PER_CELL] * timeStep;

          fptype diff = parSize - (pos.x - domainMin.x);
          if(diff > epsilon)
            cell->a[j % PARTICLES_PER_CELL].x += stiffnessCollisions*diff - damping*cell->v[j % PARTICLES_PER_CELL].x;

          diff = parSize - (domainMax.x - pos.x);
          if(diff > epsilon)
            cell->a[j % PARTICLES_PER_CELL].x -= stiffnessCollisions*diff + damping*cell->v[j % PARTICLES_PER_CELL].x;

          diff = parSize - (pos.y - domainMin.y);
          if(diff > epsilon)
            cell->a[j % PARTICLES_PER_CELL].y += stiffnessCollisions*diff - damping*cell->v[j % PARTICLES_PER_CELL].y;

          diff = parSize - (domainMax.y - pos.y);
          if(diff > epsilon)
            cell->a[j % PARTICLES_PER_CELL].y -= stiffnessCollisions*diff + damping*cell->v[j % PARTICLES_PER_CELL].y;

          diff = parSize - (pos.z - domainMin.z);
          if(diff > epsilon)
            cell->a[j % PARTICLES_PER_CELL].z += stiffnessCollisions*diff - damping*cell->v[j % PARTICLES_PER_CELL].z;

          diff = parSize - (domainMax.z - pos.z);
          if(diff > epsilon)
            cell->a[j % PARTICLES_PER_CELL].z -= stiffnessCollisions*diff + damping*cell->v[j % PARTICLES_PER_CELL].z;

          //move pointer to next cell in list if end of array is reached
          if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
            cell = cell->next;
          }
        }
      }
}
#else
void ProcessCollisionsMT(int tid)
{
  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
  {
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
	{
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
	    if(!((ix==0)||(iy==0)||(iz==0)||(ix==(nx-1))||(iy==(ny-1))==(iz==(nz-1))))
			continue;	// not on domain wall
        int index = (iz*ny + iy)*nx + ix;
        Cell *cell = &cells[index];
        int np = cnumPars[index];
        for(int j = 0; j < np; ++j)
        {
		  int ji = j % PARTICLES_PER_CELL;
          Vec3 pos = cell->p[ji] + cell->hv[ji] * timeStep;

		  if(ix==0)
		  {
            fptype diff = parSize - (pos.x - domainMin.x);
		    if(diff > epsilon)
              cell->a[ji].x += stiffnessCollisions*diff - damping*cell->v[ji].x;
		  }
		  if(ix==(nx-1))
		  {
            fptype diff = parSize - (domainMax.x - pos.x);
            if(diff > epsilon)
              cell->a[ji].x -= stiffnessCollisions*diff + damping*cell->v[ji].x;
		  }
		  if(iy==0)
		  {
            fptype diff = parSize - (pos.y - domainMin.y);
            if(diff > epsilon)
              cell->a[ji].y += stiffnessCollisions*diff - damping*cell->v[ji].y;
		  }
		  if(iy==(ny-1))
		  {
            fptype diff = parSize - (domainMax.y - pos.y);
            if(diff > epsilon)
              cell->a[ji].y -= stiffnessCollisions*diff + damping*cell->v[ji].y;
		  }
		  if(iz==0)
		  {
            fptype diff = parSize - (pos.z - domainMin.z);
            if(diff > epsilon)
              cell->a[ji].z += stiffnessCollisions*diff - damping*cell->v[ji].z;
		  }
		  if(iz==(nz-1))
		  {
            fptype diff = parSize - (domainMax.z - pos.z);
            if(diff > epsilon)
              cell->a[ji].z -= stiffnessCollisions*diff + damping*cell->v[ji].z;
		  }
          //move pointer to next cell in list if end of array is reached
          if(ji == PARTICLES_PER_CELL-1) {
            cell = cell->next;
          }
        }
      }
	}
  }
}
#endif

#define USE_ImpeneratableWall
#if defined(USE_ImpeneratableWall)
void ProcessCollisions2MT(int tid)
{
  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
  {
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
	{
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
#if 0
// Chris, the following test should be valid
// *** provided that a particle does not migrate more than 1 cell
// *** per integration step. This does not appear to be the case
// *** in the pthreads version. Serial version it seems to be OK
	    if(!((ix==0)||(iy==0)||(iz==0)||(ix==(nx-1))||(iy==(ny-1))==(iz==(nz-1))))
			continue;	// not on domain wall
#endif
        int index = (iz*ny + iy)*nx + ix;
        Cell *cell = &cells[index];
        int np = cnumPars[index];
        for(int j = 0; j < np; ++j)
        {
		  int ji = j % PARTICLES_PER_CELL;
          Vec3 pos = cell->p[ji];

		  if(ix==0)
		  {
            fptype diff = pos.x - domainMin.x;
		    if(diff < Zero)
			{
				cell->p[ji].x = domainMin.x - diff;
				cell->v[ji].x = -cell->v[ji].x;
				cell->hv[ji].x = -cell->hv[ji].x;
			}
		  }
		  if(ix==(nx-1))
		  {
            fptype diff = domainMax.x - pos.x;
 			if(diff < Zero)
			{
				cell->p[ji].x = domainMax.x + diff;
				cell->v[ji].x = -cell->v[ji].x;
				cell->hv[ji].x = -cell->hv[ji].x;
			}
		  }
		  if(iy==0)
		  {
            fptype diff = pos.y - domainMin.y;
		    if(diff < Zero)
			{
				cell->p[ji].y = domainMin.y - diff;
				cell->v[ji].y = -cell->v[ji].y;
				cell->hv[ji].y = -cell->hv[ji].y;
			}
		  }
		  if(iy==(ny-1))
		  {
            fptype diff = domainMax.y - pos.y;
 			if(diff < Zero)
			{
				cell->p[ji].y = domainMax.y + diff;
				cell->v[ji].y = -cell->v[ji].y;
				cell->hv[ji].y = -cell->hv[ji].y;
			}
		  }
		  if(iz==0)
		  {
            fptype diff = pos.z - domainMin.z;
		    if(diff < Zero)
			{
				cell->p[ji].z = domainMin.z - diff;
				cell->v[ji].z = -cell->v[ji].z;
				cell->hv[ji].z = -cell->hv[ji].z;
			}
		  }
		  if(iz==(nz-1))
		  {
            fptype diff = domainMax.z - pos.z;
 			if(diff < Zero)
			{
				cell->p[ji].z = domainMax.z + diff;
				cell->v[ji].z = -cell->v[ji].z;
				cell->hv[ji].z = -cell->hv[ji].z;
			}
		  }
          //move pointer to next cell in list if end of array is reached
          if(ji == PARTICLES_PER_CELL-1) {
            cell = cell->next;
          }
        }
      }
	}
  }
}
#endif

////////////////////////////////////////////////////////////////////////////////

void AdvanceParticlesMT(int tid)
{
  for(int iz = grids[tid].sz; iz < grids[tid].ez; ++iz)
    for(int iy = grids[tid].sy; iy < grids[tid].ey; ++iy)
      for(int ix = grids[tid].sx; ix < grids[tid].ex; ++ix)
      {
        int index = (iz*ny + iy)*nx + ix;
        Cell *cell = &cells[index];
        int np = cnumPars[index];
        for(int j = 0; j < np; ++j)
        {
          Vec3 v_half = cell->hv[j % PARTICLES_PER_CELL] + cell->a[j % PARTICLES_PER_CELL]*timeStep;
#if defined(USE_ImpeneratableWall)
		// N.B. The integration of the position can place the particle
		// outside the domain. Although we could place a test in this loop
		// we would be unnecessarily testing particles on interior cells.
		// Therefore, to reduce the amount of computations we make a later
		// pass on the perimiter cells to account for particle migration
		// beyond domain
#endif
          cell->p[j % PARTICLES_PER_CELL] += v_half * timeStep;
          cell->v[j % PARTICLES_PER_CELL] = cell->hv[j % PARTICLES_PER_CELL] + v_half;
          cell->v[j % PARTICLES_PER_CELL] *= 0.5;
          cell->hv[j % PARTICLES_PER_CELL] = v_half;

          //move pointer to next cell in list if end of array is reached
          if(j % PARTICLES_PER_CELL == PARTICLES_PER_CELL-1) {
            cell = cell->next;
          }
        }
      }
}

////////////////////////////////////////////////////////////////////////////////

void AdvanceFrameMT(int tid)
{
  //swap src and dest arrays with particles
  if(tid==0) {
    std::swap(cells, cells2);
    std::swap(cnumPars, cnumPars2);
  }
  pthread_barrier_wait(&barrier);

  ClearParticlesMT(tid);
  pthread_barrier_wait(&barrier);
  RebuildGridMT(tid);
  pthread_barrier_wait(&barrier);
  InitDensitiesAndForcesMT(tid);
  pthread_barrier_wait(&barrier);
  ComputeDensitiesMT(tid);
  pthread_barrier_wait(&barrier);
  ComputeDensities2MT(tid);
  pthread_barrier_wait(&barrier);
  ComputeForcesMT(tid);
  pthread_barrier_wait(&barrier);
  ProcessCollisionsMT(tid);
  pthread_barrier_wait(&barrier);
  AdvanceParticlesMT(tid);
  pthread_barrier_wait(&barrier);
#if defined(USE_ImpeneratableWall)
  // N.B. The integration of the position can place the particle
  // outside the domain. We now make a pass on the perimiter cells
  // to account for particle migration beyond domain.
  ProcessCollisions2MT(tid);
  pthread_barrier_wait(&barrier);
#endif
}

#ifndef ENABLE_VISUALIZATION
void *AdvanceFramesMT(void *args)
{
  thread_args *targs = (thread_args *)args;

  for(int i = 0; i < targs->frames; ++i) {
    AdvanceFrameMT(targs->tid);
  }

  return NULL;
}
#else
//Frame advancement function for worker threads
void *AdvanceFramesMT(void *args)
{
  thread_args *targs = (thread_args *)args;

#if 1
  while(1)
#else
  for(int i = 0; i < targs->frames; ++i)
#endif
  {
    pthread_barrier_wait(&visualization_barrier);
    //Phase 1: Compute frame, visualization code blocked
    AdvanceFrameMT(targs->tid);
    pthread_barrier_wait(&visualization_barrier);
    //Phase 2: Visualize, worker threads blocked
  }

  return NULL;
}

//Frame advancement function for master thread (executes serial visualization code)
void AdvanceFrameVisualization()
{
    //End of phase 2: Worker threads blocked, visualization code busy (last frame)
    pthread_barrier_wait(&visualization_barrier);
    //Phase 1: Visualization thread blocked, worker threads busy (next frame)
    pthread_barrier_wait(&visualization_barrier);
    //Begin of phase 2: Worker threads blocked, visualization code busy (next frame)
}
#endif //ENABLE_VISUALIZATION

////////////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
#ifdef PARSEC_VERSION
#define __PARSEC_STRING(x) #x
#define __PARSEC_XSTRING(x) __PARSEC_STRING(x)
        std::cout << "PARSEC Benchmark Suite Version "__PARSEC_XSTRING(PARSEC_VERSION) << std::endl << std::flush;
#else
        std::cout << "PARSEC Benchmark Suite" << std::endl << std::flush;
#endif //PARSEC_VERSION
#ifdef ENABLE_PARSEC_HOOKS
  __parsec_bench_begin(__parsec_fluidanimate);
#endif

  if(argc < 4 || argc >= 6)
  {
    std::cout << "Usage: " << argv[0] << " <threadnum> <framenum> <.fluid input file> [.fluid output file]" << std::endl;
    return -1;
  }

  int threadnum = atoi(argv[1]);
  int framenum = atoi(argv[2]);

  //Check arguments
  if(threadnum < 1) {
    std::cerr << "<threadnum> must at least be 1" << std::endl;
    return -1;
  }
  if(framenum < 1) {
    std::cerr << "<framenum> must at least be 1" << std::endl;
    return -1;
  }

#ifdef ENABLE_CFL_CHECK
  std::cout << "WARNING: Check for Courant–Friedrichs–Lewy condition enabled. Do not use for performance measurements." << std::endl;
#endif

  InitSim(argv[3], threadnum);
#ifdef ENABLE_VISUALIZATION
  InitVisualizationMode(&argc, argv, &AdvanceFrameVisualization, &numCells, &cells, &cnumPars);
#endif

#ifdef ENABLE_PARSEC_HOOKS
  __parsec_roi_begin();
#endif
#if defined(WIN32)
  thread_args* targs = (thread_args*)alloca(sizeof(thread_args)*threadnum);
#else
//  thread_args targs[threadnum];  // DMITRII KUVAISKII: VLA stack arrays are broken in ICC-MPX
  thread_args targs[256]; // max number of threads
#endif
  for(int i = 0; i < threadnum; ++i) {
    targs[i].tid = i;
    targs[i].frames = framenum;
    pthread_create(&thread[i], &attr, AdvanceFramesMT, &targs[i]);
  }

  // *** PARALLEL PHASE *** //
#ifdef ENABLE_VISUALIZATION
  Visualize();
#endif

  for(int i = 0; i < threadnum; ++i) {
    pthread_join(thread[i], NULL);
  }
#ifdef ENABLE_PARSEC_HOOKS
  __parsec_roi_end();
#endif

  if(argc > 4)
    SaveFile(argv[4]);
  CleanUpSim();

#ifdef ENABLE_PARSEC_HOOKS
  __parsec_bench_end();
#endif

  return 0;
}

////////////////////////////////////////////////////////////////////////////////

redpajama_set_name: RedPajamaGithub


Even though the Dual 4 columns are based on a very stable monolithic silica material, the use of the Dual 4 Guard column is recommended to increase the security of the analytical column even further. The Dual 4 precolumn consists of a PEEK cartridge which is also filled with monolithic silica gel. The cartridge is easy to exchange and is contained in an aluminum holder which is connected directly to the analytical column. This proven «On-Column Guard System is easy to use and additionally minimizes the dead volume.


text:
La OJSC «United Aircraft Corporation» («UAC») -  - è un raggruppamento di aziende aeronautiche e aerospaziali russe creato nel 2006 su iniziativa del governo russo.

Storia
La JSC «UAC» è stata istituita in conformità con il decreto presidenziale del 20 febbraio 2006 nº 140 chiamato «Società per azioni "United Aircraft Corporation» allo scopo di conservare e sviluppare il potenziale scientifico e produttivo dell'industria aeronautica russa, la sicurezza e la difesa nazionale e la concentrazione delle risorse intellettuali, industriali e finanziare.

Oggi «UAC» comprende circa 30 aziende ed è uno dei più grandi attori del mercato del trasporto aereo globale. Le aziende nella struttura della nuova società hanno conservato i precedenti marchi come: Sukhoi, Mikoyan, Ilyushin, Tupolev, Yakovlev, Beriev, ecc.

Nell aprile 2015, la società ha cambiato nome passando da una joint-stock company (JCC) () a una open joint-stock company (OJSC) ().

Composizione
Struttura della United Aircraft Corporation al 2015 e le società più rilevanti :
 Sukhoi Group
 57,06% JSC «Company Sukhoi»
 53,69% CJSC «Sukhoi Civil Aircraft»
 57,06% CJSC «Sukhoi new civil technologies»
 Irkut Group
 91,37% JSC «Irkut Corporation»
 78,73% JSC «OKB Imeni A.S. Yakovlev»
 83,99% CJSC «Beta-Ir»
 Altre società
 94,37% JSC «Tupolev»
 94,21% JSC «TANTK Imeni G.M. Berieva»
 66,86% JSC «RSK Mikoyan Gurevich»
 100% JSC «Myasishchev Design Bureau»

Azionisti
Azionisti della United Aircraft Corporation al 10/08/2015 :
 90,3% - Federal Agency for State Property Management
 5,6% - Vnesheconombank
 4,1% - Investitori privati

Aeromobili
Aeromobili commercializzati dalla United Aircraft Corporation al 2017 :

 Aerei civili
 Ilyushin Il-96-300 (lungo raggio)
 Tupolev Tu-214 (medio raggio)
 Tupolev Tu-204CM (medio raggio)
 Irkut MS-21 (medio raggio)
 Sukhoi Superjet 100 (corto raggio)
 Ilyushin Il-114-300 (corto raggio)
 CRAIC CR929 (lungo raggio)

 Aerei militari
 Tupolev Tu-95MC (bombardiere strategico a lungo raggio)
 Tupolev Tu-160 (bombardiere strategico a lungo raggio)
 Tupolev Tu-22M3 (bombardiere strategico a medio raggio)
 Sukhoi Su-27SK (caccia multiruolo)
 Sukhoi Su-30MK (caccia multiruolo)
 Sukhoi Su-30SM (caccia multiruolo)
 Sukhoi Su-32 (cacciabombardiere)
 Sukhoi Su-33 (caccia multiruolo imbarcato)
 Sukhoi Su-34 (cacciabombardiere)
 Sukhoi Su-35 (caccia multiruolo)
 Sukhoi PAK FA (T-50) (caccia multiruolo 5ª gen.)
 Mikoyan MiG-29K/KUB (caccia multiruolo imbarcato 4++ gen.)
 Mikoyan MiG-35 (caccia multiruolo 4++ gen.)
 Yakovlev Yak-130 (addestratore avanzato)
 Yakovlev Yak-152 (addestratore basico)

 Aerei da trasporto
 Ilyushin Il-76MD-90A (pesante)
 Ilyushin Il-214 (MTA) (medio)
 Ilyushin Il-112V (medio-leggero)

 Aerei per usi speciali
 Beriev Be-200 (anfibio)

Note

Altri progetti

Collegamenti esterni

Aziende aeronautiche russe
Aziende russe del settore difesa

redpajama_set_name: RedPajamaWikipedia


Board a bus from Kuala Lumpur to Jitra and indulge yourself in all its awe. Its authentic ambiance makes comfortable bus journeys even more worthwhile, equating to nothing less than an effortless travel getaway.
Passengers are guaranteed a memorable bus travel from Kuala Lumpur to Jitra with CatchThatBus. Get your money's worth when you climb aboard some of our finest operators, with prices as low as MYR 50.0.
What is the bus fare from Kuala Lumpur to Jitra ?
Bus fares from Kuala Lumpur to Jitra range from MYR 50.0 to MYR 50.0 depending on the coach class.
What is the lowest bus fare from Kuala Lumpur to Jitra ?
Currently, the lowest bus fare from Kuala Lumpur to Jitra is MYR 50.0.
How many buses currently running from Kuala Lumpur to Jitra ?
There are 4 buses running from Kuala Lumpur to Jitra.
How many bus operators operate from Kuala Lumpur to Jitra ?
What's the duration from Kuala Lumpur to Jitra by bus ?
The travel duration for buses departing from Kuala Lumpur to Jitra takes 6 H 57 M. This is subject to unforeseen factors which may prolong the journey.
CatchThatBus is your one stop bus booking platform for bus tickets on-the-go. Now, travelers can easily travel to various destinations including Kuala Lumpur to Jitra . Book your bus tickets today from our website, mobile website or app, free for download from Google Play Store and App Store.


Offshore Casting Alongside The Major Arts And Crafts Np-Jack!
If y'all haven't noticed, Major Craft has quietly launched a funky novel flagship saltwater hit of rods: the NP-JACK. Of the serial the latest delineate of piece of occupation consists of around sick novel Offshore Casting rods. Obviously nosotros were over-excited in addition to striking the H2O inwards no fourth dimension alongside novel gear inwards hand!
After a long struggle nosotros flora it to hold upwards a pesky Shark!
It is the fish that has kept fishermen flocking dorsum for more, dazzled yesteryear its speed, tenacity in addition to sheer aggression when rainfall down a quick retrieved lure. That lure inwards enquiry is no dubiety the Jackson Pintail Tune. Gear up, for the flavor is inwards total swing!
0 Response to "Offshore Casting Alongside The Major Arts And Crafts Np-Jack!"


Same Day Rush orders must be placed before 3:00pm EST, unless approved over the phone. You must choose e-mail delivery for same day deliveries.
At cost, where applicable. Contact us for more information.
Delivery times do not include shipping time. For fastest delivery, please choose e-mail delivery. Prices do not include copyright clearance costs. Please see our Copyright Policy for more information. Additional costs and restrictions may apply for certain types of articles, please contact us for more information.


text:
Just SING
Just SING! Christmas Vol. 2
The most wonderful time of the year for download : Just SING! Christmas Vol.2
DSiWare [ DTP Entertainment - Doetinchem ]
Right on time for the festive season dtp young entertainment publishes a second Christmas karaoke game on the Nintendo DSiWare service: 'Just SING! Christmas Vol. 2'. This is where players will find two of the most popular Christmas songs: 'We wish you a merry Christmas' and 'Deck the Halls'
Vocal chords lubricated, handheld at the ready and off you go: 'Just SING! Christmas Vol. 2' will get you into the mood for the Advent period. Players will get into the festive spirit in the best karaoke style with 'We wish you a merry Christmas' and 'Deck the Halls'. For those who just can't get enough the previous 'Just SING! Christmas Songs' title is also available for download from the Nintendo DSiWare Shop. Karaoke fans can join in there with classics like 'Silent Night' and 'Jingle Bells'. The icing on the cake: Players are filmed by their Nintendo DSi™ and are projected as a livestream onto the videowall background behind their singing avatar. Christmas time – bring it on!
To enjoy the festive spirit, players simply have to connect to the Nintendo DSiWare Shop using their handheld. Thanks to the intuitive control, downloading is child's play.
The title 'Just SING! Christmas Vol. 2' is now available as a Nintendo DSiWare game for 200 Nintendo DSi Points. It has been developed in Doetinchem, the Netherlands by Engine Software B.V.
The 'Just SING!' titles represent the up to now unique conversion of a karaoke game for the Nintendo DS and Nintendo DSi systems
Players are filmed by their Nintendo DSi and are projected onto the videowall background behind their singing avatar.
Immediate feedback on the singer's performance while they are singing
A special noise filter enables a high quality karaoke experience via the Nintendo DSi
Images de Just SING! Christmas Vol. 2
Vidéo de Just SING! Christmas Vol. 2
Titre : Just SING! Christmas Vol. 2
Editeur : DTP Entertainment
Studio : Doetinchem
Plateformes : DSiWare
About dtp young entertainment
dtp young entertainment GmbH & Co. KG was founded in December 2004. The Hamburg based company publishes trendy child, youth and family themed titles for console, mobile and PC platforms. At the heart of the dtp young portfolio are strong international licenses such as 'Emily the Strange™' and 'America's Next Top Model™'. Standalone titles like 'Just SING!' and 'Just SING! Vol. 2', the world's first ever karaoke games for the Nintendo DSi™ and 'Dance! It's your Stage' featuring Detlef D! Soost also belong to its core business. In addition the games from the nationally and internationally marketed key series labels, 'Horse & Foal, 'ANIKIDS', 'Classics To Go', 'J4G – JUST FOR GIRLS' and 'GripsKids' form an important component of the publisher's offer
Votre avis sur Just SING! Christmas Vol. 2
Just SING : dernières infos
16-09Just Sing est désormais disponible sur PS4 et Xbox One 16-09Just Sing est désormais disponible 16-08Ubisoft annonce Just Sing, un nouveau jeu musical


Getting into green DIY this Earth Day.
Going Green is more than just a trend, it is a responsibility. This Earth Day, we have the responsibility to share our love for green DIY with you. Here are some of our favourite green DIY blogs for you to follow to get your greening on track.
Founded in 2011, Dream Green DIY by Carrie Waller is a lifestyle and crafting blog. With a modern funk and conscious twist, this blog will inspire you to do all sorts of greening creative projects.
Top read from this blog – Our Master Bedroom Makeover Reveal.
For the green conscious fellows out there, The Green Living Guy is the perfect blog for you to wrap your head around how to go and stay green. Run by best-selling author, editor and expert on Green-Eco Friendly lifestyles, Seth Leitman, The Green Living Guy keeps you in the loop when it comes to the latest green living news and DIY ideas.
Top read from this blog – How to incorporate eco-friendly living into your apartment.
Attention working moms! We have just the blog for you. Momtrepreneur is a mommy blog that covers all aspects of motherhood, including our favourite subject, DIY! Claire Minnaar writes about fun do-it-yourself projects and gives some great eco-friendly ideas you can do with your kids.
Top read from this blog – Recycling a garden hose.
4. This Chick Is Eco-Friendly, For Sure!
If a soulful home is what you want, this blog is sure to help you create it! Eco-chick is a blog focused purely of living green. From DIYing your home to green gifting ideas, you will find all the inspiration you need on this blog.
Top read from this blog – Fresh-up your bathroom with 7 killer green ideas.


For other salt forms or metabolites please inquire. Fluorescent cGMP analogue (λexc 649 nm, λem 666 nm), e.g. for research focussed on kinetic studies of CNG ion channels and other cGMP-reponsive binding proteins. Detailed technical information available.


A construction company based in Lincolnshire was fined after a contractor was seriously injured in an accident at work incident.
M & L Installers Ltd had been contracted to install a mezzanine floor at a factory in Surrey. The design of the floor included a hole so that an elevator could be fitted at a later date.
Guildford Crown Court heard how a contractor who was working on the floor stepped backwards and fell through the hole. He sustained life threatening injuries after falling 3.5 meters to the concrete floor below.
The contractor suffered a severe brain injury for which he was kept in hospital for 2 months. He has been unable to return to work since the incident on 13 January 2015.
An investigation into the accident at work incident by the Health and Safety Executive (HSE) found that the firm had failed to board up or guard the hole to prevent someone from falling through.
M & L Installers Ltd pleaded guilty to a breach of the Work at Height Regulations, Regulation 6(3). The construction firm were fined £20,000 and ordered to pay £9,165.56 in costs.
"The contractors injuries were life changing and he could have easily been killed. This serious incident and devastation could have been avoided if basic safe guards had been put in place".


Triple jump -- Holback (WG) 42-8.
Records -- Sobrato 6-1; Willow Glen 7-0.
Triple jump -- Wong (SC) 42-4.5.
Record -- Silver Creek 4-2-1.
Triple jump -- Nguyen (EV) 43-9.5.
Record -- Evergreen Valley 7-0.
High jump -- Yeager (LA) 5-8; Long jump -- Yeago (LA) 20-4; Triple jump -- Bernardo (M) 39-9.75.
Triple jump -- Thomas (SC) 42-1.5.
Pole vault -- Cesare (WG) 10-6; Long jump -- Emanuel (WG) 15-4; Triple jump -- Emanuel (WG) 31-4.
Record -- Willow Glen 3-4.
High jump -- Johal (SC) 4-4; Pole vault -- ; Long jump -- Torrecampo (SC) 15-9.5; Triple jump -- Francisco (SC) 34-1.
Record -- Silver Creek 3-3-1.
Long jump -- Nguyen (EV) 15-6.5; Triple jump -- Wetzel (EV) 33-1.5.
Record -- Evergreen Valley 4-3.
High jump -- Roberto (M) 4-6; Long jump -- Lien (M) 15-9; Triple jump -- Edusada (M) 32-8.
Records -- Los Altos 1-5; Milpitas 2-4.
Triple jump -- Kaneda (MV) 32-5.5.
Triple jump -- Carbin (MP) 41-6.5.
Record �� Santa Teresa 4-2-1.
Triple jump -- Chou (Leland) 38-10.
Triple jump -- Cota (B) 41-11.
Triple jump -- Cody Kelly (W) 40-8.5.
High jump -- Cano (O) 5-4; Long jump -- Gabband (LO) 19-3; Triple jump -- Gabband (LO) 41-11.
High jump -- Tzur (PA) 5-8; Long jump -- Du (PA) 21-6; Triple jump -- Price (G) 43-10.75.
High jump -- Luong (W) 5-9; Long jump -- Zhao (C) 19-11; Triple jump -- Mitchell (W) 42-5.50.
High jump -- Cabuag (ST) 4-7; Pole vault �� Shiroma (ST) 8-6; Long jump -- Oxford (ST) 16-1; Triple jump -- Sanders-Curry (ST) 34-10.5.
Triple jump -- Sanders-Curry (ST) 34-10.5.
High jump -- McCarthy (P) 5-0; Pole vault -- Baxter (B) 8-6; Long jump -- McCarthy (P) 15-7; Triple jump -- Lawrence (B) 32-10.
Pole vault �� Hendel (W) 11-0; Long jump -- Brouse (W) 15-2.75; Triple jump -- Brouse (W) 33-1.
Long jump -- Nelson (O) 13-3; Triple jump -- Guizar (O) 31-10.5.
High jump -- Piearcy (C) 4-9; Long jump -- M. Kwiatkowski (W) 17-1; Triple jump -- A. Kwiatkowski (W) 33-4.
Records -- Wilcox 5-1; Cupertino 4-2.
High jump -- Thomas (SF) 5-10; Pole vault -- Toney (SF) 15-6; Long jump -- Kl. Johnson (SF) 21-3; Triple jump -- Bush (BCP) 43-11.
Records -- St. Francis 5-1; Bellarmine College Prep 6-0.
Long jump -- Jaques (P) 15-7; Triple jump -- Johnson (SF) 35-7; High jump -- Johnson (SF) 5-2; Pole vault -- Jaques (P) 12-0.
Records -- St. Francis 5-0; Presentation 2-3.


text:
\section{Introduction}
\input{sections/2-introduction}

\section{Related Work}
\input{sections/3-related_work}

\section{Problem Statement}
\input{sections/4-problem-statement.tex}

\section{Minimal Work Grasp Quality Metric}
\input{sections/5-methodology}

\section{Algorithm}
\input{sections/6-implementation.tex}

\section{Experiments and Results}
\input{sections/7-experiments}

\section{Discussion and future work}
\input{sections/8-discussion}

\input{sections/10-ackowledgement.tex}

\bibliographystyle{IEEEtran}

\section{Acknowledgments}
\footnotesize
This research was performed at the AUTOLAB at UC Berkeley in affiliation with the Berkeley AI Research (BAIR) Lab, Berkeley Deep Drive (BDD), the Real-Time Intelligent Secure Execution (RISE) Lab, and the CITRIS ``People and Robots" (CPAR) Initiative. The authors were supported in part by the Scalable Collaborative Human-Robot Learning (SCHooL) Project, NSF National Robotics Initiative Award 1734633 and by donations from Google, Siemens, Amazon Robotics, Toyota Research Institute, Autodesk, ABB, Samsung, Knapp, Loccioni, Honda, Intel, Comcast, Cisco, Hewlett-Packard and by equipment grants from PhotoNeo and NVIDIA. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the Sponsors. We thank our colleagues who helped with experiments and provided helpful feedback and suggestions, especially William Wong, Priya Sundaresan, Harry Zhang, Jackson Chui, Kate Sanders, and Andrew Lee.

\subsection{Grasping rigid objects}
A common grasp quality metric, force closure \cite{nguyen1988constructing}, evaluates a grasp by whether it can resist any 6D disturbance wrenches with an arbitrarily large grasp force.
The volume of a grasp wrench space (GWS) \cite{li1988task} and the $\epsilon$-metric \cite{ferrari.1992} are also widely used to quantify grasp quality.
While the volume reflects the quality of the entire grasp, the $\epsilon$-metric identifies the weakest point of a grasp, as $\epsilon$ is the shortest distance between the origin and any facet of the GWS.
The GWS used to evaluate grasps is typically constructed with a bounded sum-magnitude of grasp forces for computational efficiency.
Krug et al. \cite{krug2017grasp} suggest that such a construction is over-conservative for fully actuated grippers, as the force of each jaw is limited independently.

External disturbance wrenches can be estimated for specific tasks.
Task-oriented grasp quality metrics are, therefore, better suitable to estimate wrench resistance for such tasks.
A task wrench space (TWS) describes expected disturbance wrenches during a manipulation task.
The TWS is typically modeled with possible wrenches that can be imposed on an object \cite{pollard1994parallel,borst2004grasp}, or an 6D ellipsoid \cite{li1988task}.
The quality of a grasp is the scale of the TWS, so that it just fits into the GWS.
In addition, the quality can be measured with the minimal required force for a task \cite{howard2000intelligent} or minimal coefficient of friction \cite{jameson1985analytic}.
Lin and Yu \cite{lin2015grasp} observe that some disturbance wrenches happen more often than others during a task execution and, therefore, select the grasp whose GWS covers most frequent disturbances.
They further select the optimal grasp in \cite{lin2015task}, which minimizes the required motion effort of the end effector to fulfill a certain task.

\subsection{Grasping deformable objects}
Manipulating deformable objects is an active area, with applications such as food handling \cite{long2014force}, fabric manipulation \cite{li2015folding,seita2019bedmake}, and elastic rod manipulation \cite{bretl2014quasi}.
When a frictionless grasp immobilizes a rigid object, it is defined as form closure.
Gopalakrishnan and Goldberg \cite{gopalakrishnan2005d} generalize this concept to holding deformable objects with frictionless contacts, where a grasp is defined as deform closure, when positive work is required to release the object.
Wakamatsu~et~al.~\cite{wakamatsu1996static} introduce the bounded force closure metric, which guarantees a force closure grasp under a maximal allowable external force.
Delgado~et~al.~\cite{delgado2015tactile} reduce object deformation for a holding task by computing the maximum allowed force to be exerted on an object.
Jia~et~al.~\cite{jia2014grasping} propose a grasping strategy to squeeze holding deformable planar objects based on work performed by the jaws.
When two jaws squeeze and immobilize an object, and a third jaw tries to break the grasp by pushing the object, the translations of the two pushing jaws that minimize the required work to balance the object is selected.
Since the metric targets planar objects, the 3D geometry or the gravity is not considered and a point contact model is used for friction analysis. Lin~et~al.~\cite{lin2015picking,lin2014picking} address the problem of lifting a deformable object based on an object mesh model and jaw positions.
An FEM formulation computes the object deformation based on the jaw displacements.
The object will be lifted if the majority of the contact points are sticking.
Similarly, Zaidi~et~al.\cite{zaidi2017model} use FEM simulation to manipulate objects with large deformations, such as objects made of foam or rubber.
Alt~et~al \cite{alt.2016} use FEM simulation and heuristics to plan grasps for deformable thin-walled objects.

Inspired by \cite{gopalakrishnan2005d} and \cite{jia2014grasping}, the proposed minimal work quality metric optimizes grasp placements to manipulate 3D deformable hollow objects.
Furthermore, the metric is suitable for many tasks that can be modeled as target wrenches to be resisted.

\subsection{Overview}
We consider the problem of grasp planning for 3D deformable hollow objects with compliant jaw pads based on the ability of a grasp to resist target wrenches and the deformability of the object at the grasp location.

\subsection{Assumptions}
We make the following assumptions:
\begin{enumerate}
	\item The geometry and the deformability are known for the objects to be grasped.
	\item Quasi-static analysis and Coulomb friction with a known coefficient of friction.
	\item A linear elastic model of soft jaw pads and objects.
\end{enumerate}

\subsection{Notation}
\begin{itemize}
    \item $\boldsymbol{\mathsf{w}} \in \mathbb{R}^6$: wrench of a contact, which is concatenated by a 3D force and a 3D torque.
    \item $\mathcal{C}_i$: the constraint set that limits the maximum possible friction wrench and the wrench impressed by the normal pressure of the $i$-th contact.
    \item $\work$: the work performed by the gripper jaws
    \item $\boldsymbol{t} \in \mathbb{R}^6$: a target wrench to be resisted with a grasp

\end{itemize}

\subsection{Metrics}
\mysum{add description for grasp reliability}
We compare the proposed minimal work grasp quality metric with two widely used metrics.
Each grasp is computed under $K$ perturbations of grasp poses to address uncertainties in actuation.
\begin{enumerate}
    \item Grasp reliability metric $q_r$: $r = 1$ if the grasp is able to resist the target wrench $\t \in \mathbb{R}^6$ without exceeding the maximum closing force and $r = 0$ otherwise:
    \begin{equation*}
        q_r = \frac{1}{K}\sum_{k=1}^K r_k.
    \end{equation*}
    \item Minimal force metric $q_f$: The minimal required grasp force to resist $\t$:
\begin{equation*}
    q_f = \frac{1}{K}\sum_{k=1}^K \left(1 - \frac{F_k}{F_{\max}}\right).

\end{equation*}
where $F_{\max}$ is the maximal grasp force used in the experiments.
\end{enumerate}

\subsection{Task modeling}
Two manipulation tasks are considered: 1) vertical lifting and 2) lifting and $90^\circ$  rotation.
We model each task with a 6D gravity wrench to be resisted under multiple object poses obtained by discretizing the trajectory of the task.
A single pose is considered for the lifting task, since the gravity wrench remains unchanged during the manipulation, while three poses are considered for the lifting and rotation task.

\subsection{Objective}
We use the balanced accuracy score to evaluate the prediction accuracy of each quality metric by comparing them with real-world grasps.
The predicted grasp success is binary and is true if the metric is higher than a threshold.
The balanced accuracy is suitable for imbalanced datasets and is computed with the raw accuracy, where each sample is weighted with the inverse prevalence of its true class.

\subsection{Grasp force computation for a known task}
\mysum{compute grasp force}

For a grasp with $N$ contacts, denote $\matr{G} \in \mathbb{R}^{6 \times 6N}$ as the grasp matrix, $\boldsymbol{w}_i \in \mathbb{R}^6$ as the wrench applied at the $i$-th contact, and $\boldsymbol{F} = [F_1,\dots,F_N]^T$ as a vector of grasp forces, where $F_i$ is the grasp force at the $i$-th contact.
The minimal required grasp force to resist a target wrench $\t$ is computed by:
\begin{equation}
\begin{aligned}
& \underset{\boldsymbol{F},\{\boldsymbol{\mathsf{w}}_1,\ldots,\boldsymbol{\mathsf{w}}_N\}}{\text{minimize}}
& & \boldsymbol{F} \bigcdot \boldsymbol{1}_{N} \\
& \text{subject to}
& & \matr{G} \begin{bmatrix} \boldsymbol{w}_1 \\ \vdots \\ \boldsymbol{w}_N \end{bmatrix} = \t, \\
&&& \boldsymbol{\mathsf{w}}_i \in \mathcal{C}_i, \hspace{0.1em} \forall{i}.
\end{aligned}
\label{eq:sol_min_force}
\end{equation}

\mysum{work and quality }

Based on Hooke's law, the work $\work$ is computed by:
\begin{equation}
	\work = \sum_{i=1}^N F_i \bigcdot d_i \text{, with } d_i = \frac{F_i}{s_{o_i}} + \epsilon,
\end{equation}
where $d_i$ is the displacement of the $i$-th jaw and $s_{o_i}$ is the object stiffness at contact~$i$.
$\epsilon$ is a small number, such that the minimal work quality metric is applicable to rigid objects or objects contain a rigid part.
The displacement $d_i = \epsilon$ and the minimal work quality reduces to comparing minimal force between grasps.

Finally, the quality $q_w$ of a grasp under $K$ perturbations of grasp pose is computed by:
\begin{equation}
    q_w = \frac{1}{K} \sum_{k=1}^K \left(1 - \frac{\work_k}{\work_{\max}}\right),
\end{equation}
where $\work_{\max}$ is used for normalization and is the the product of maximal grasp force and object's deformation of collected data.

\subsection{REACH: contact geometry}
Danielczuk et al.~\cite{danielczuk2019reach} proposed the Robust Efficient Area Contact Hypothesis (REACH) model for contact profile estimation between soft jaw pads and rigid objects.
Given an object's geometry modeled as a triangular mesh, the contact area is computed as the constructive solid geometry intersection of the extruded polygon of the jaw with the object.
The intersection estimates the deformation of the soft pad around the object at each point on the contact and the pressure distribution linearly scales with the gripper pad deformation.
The REACH model provides contact area consists of triangles and the normal pressure of each triangle.

We apply the REACH model to estimate contact information between compliant jaw pads and deformable hollow objects due to its highly computational efficiency compare to e.g. the Finite Element Method.
We note that the obtained contact details may not be accurate for objects with high deformability.

\subsection{6D ellipsoidal limit surface}
Grasps with soft jaws may result in a non-planar contact area, where the friction wrench applied at the contact is six-dimensional (6D).
This work uses the 6D ellipsoid proposed in \cite{xu2019tro} as the limit surface model.
Note that other friction models are applicable to the proposed work computation algorithm as well.

Friction depends on relative body motion.
For a given contact area and pressure distribution, possible friction wrenches are obtained by sampling the instantaneous relative motion, defined as body twist in screw theory \cite{murray.2017}.
The direction of frictional force of each triangle is obtained by projecting the velocity onto the triangle plane, while the magnitude is the product of the coefficient of friction and the normal force applied at the triangle.
The frictional torque is computed with respect to the friction-weighted center of pressure.
By summing up the friction contribution of each triangle, we obtain a friction wrench of the contact for each sampled twist.

We fit the friction wrenches to a 6D ellipsoid by solving a convex optimization.
The friction wrench is constrained to be in the interior of the ellipsoid.
The surface of the fitted ellipsoid is then evenly sampled to acquire linear constraints.

We use a 6D ellipsoidal limit surface to model the friction wrenches of each contact.
Given the ellipsoid matrix $\matr{A} \in \mathbb{R}^{6 \times 6}$, the friction wrenches $\f \in \mathbb{R}^6$ are constrained by:
\begin{equation}
    \f ^T \matr{A} \f \leq 1.
\end{equation}

For computational efficiency, we compute the linearized constraints $\mathcal{C}_i$ of the $i$-th contact by evenly sample the ellipsoid with $M$ points $\boldsymbol{p} \in \mathbb{R}^{6 \times M}$ on the surface of $\matr{A}$.
Each point and the outward normal of $\matr{A}$ at the point form a hyperplane.
The friction wrenches are constrained in the interior of the $M$ hyperplanes:
\begin{equation}
\begin{aligned}
        \mathcal{C}_i = \{\boldsymbol{\mathsf{w}}_i \in \mathbb{R}^6 \ | \ \boldsymbol{n} & \bigcdot \left( \boldsymbol{\mathsf{w}}_i - \fperp \right)  \leq \boldsymbol{n} \bigcdot  \boldsymbol{p} \},  \\
        \text{with } \boldsymbol{n} &= \matr{A}  \boldsymbol{p},
\end{aligned}
\end{equation}
where $\fperp \in \mathbb{R}^6$ is the wrench impressed by the normal pressure, $\boldsymbol{n}$ are the normals of $\matr{A}$ at $\boldsymbol{p}$.

\subsection{Acquisition of object stiffness}
\label{sec:stiffness_acquisition}
The object stiffness is required to compute the required work of gripper jaws for a specific task.
We estimate the object stiffness by means of its 3D mesh and physical experiments.
One can use e.g. the Finite Element Analysis to compute the object deformation with a closing force of the gripper.
However, the deformability of e.g. plastic bottles and cups highly depends on the wall thickness, the geometry, and material of the object, which are non-trivial to obtain.
Therefore, we use a physical robot to collect object's stiffness at different locations in this work.

We estimate the object's stiffness based on 1) a known gripper closing force $F_c$, 2) the gripper opening $L_s$ when its just in contact with the object, and 3) the opening $L_e$ when $F_c$ is reached.
We first plan antipodal grasps in simulation for each object.
$L_s$ is estimated by checking the minimal distance between the 3D object mesh and the gripper mesh at the grasp location.

At each planned grasp location, the Robotiq 2F-85 gripper closes with a minimal possible force $F_c=20N$.
The object's stiffness $s_{o_i}$ at the location $i$, obtained by the two intersection points of the grasp axes and the object surface is computed by:
\begin{equation*}
s_{o_i} = \frac{F_c}{L_{s_i} - L_{e_i}}.
\end{equation*}
We repeat each grasp 5 times and the median of the collected gripper opening after reaching the closing force is selected as $L_e$.

We note that the actual grasp force of the Robotiq gripper depends on the object's material and the gripper closing speed.
Fig. \ref{fig:repeatability} shows experiment results of the repeatability of the gripper.
An object with an open lid is grasped at five locations, where the object has different deformability at each location.
The gripper closes repeatedly with the minimal force and multiple speeds, ranges from 0 to 255, where 0 is the lowest speed.
For each grasp location and each closing speed, the gripper closes 15 times and the gripper opening (cm) is recorded after the force is reached.
We restart the gripper program every three closes.

Fig. \ref{fig:repeatability} shows the mean and standard deviation of the gripper opening at each grasp location with a closing speed.
Results show the the gripper opening has a higher error at grasp pose 1-3 than pose 4-5.
This observation suggests when the gripper opening is small, e.g. when the object is thin or has large deformation, the gripper repeatability is lower than with a large gripper opening.

Although the absolute gripper opening and the collected object's stiffness may not be highly accurate, the relative object's stiffness is reasonable, which is essential to compute optimal grasp placements.

\input{sections/figures/robotiq_repeatability.tex}

\subsection{Simulation results: Planned grasps with three quality metrics}

We plan grasps on five objects based on their 3D meshes and the interpolated stiffness maps, as shown in Fig. \ref{fig:metrics}(a) and (b), where two objects are 3D printed using NinjaFlex TPU flexible filament since we want to experiment with objects of various shapes and materials.
Note that some inaccuracies of the measured stiffness exist, such as the rigid cap of the object~1 and the neck of object~2.

We sample antipodal grasps candidates and compute grasp quality for 1) vertical lifting and 2) lifting and $90^\circ$ rotation task.
Three quality metrics are compared: grasp reliability, minimal force, and minimal work, as shown in Fig. \ref{fig:metrics}.
The two tasks are modeled with a 6D gravity wrench to be resisted under one and three object poses obtained by discretizing the manipulation trajectory, respectively, since the gravity wrench remains the same for the vertical lifting task.
The metric of each grasp is computed under 20 perturbations of grasp poses for each object pose of the task.
The lowest quality value of a grasp among all object poses is selected as the value for each metric.

Fig. \ref{fig:metrics} suggests the planned grasps with the proposed minimal work quality metric avoid causing large deformations of the object, while are able to resist the gravitational disturbances of the manipulation tasks.

\input{sections/figures/quality_metrics.tex}
\input{sections/figures/fig_phys_exp.tex}

\input{sections/figures/tab_phys_exp.tex}
\subsection{Physical experiments}
We evaluate the planned grasps for three representative objects (object 1,3,5) with physical experiments for the two manipulation tasks.
We select 46 grasp poses in total for three objects that cover different regions of object and each grasp is repeated five times for each task, which sums up to 460 grasps in total.
We consider a grasp is successful if the task is completed while the object is returned into its original shape when the grasp force is released and the content is not dislodged during the grasp.

Objects are filled with wet towels to imitate the weight filled with liquid without changing the object's deformability or damage the electrical devices.
Object 1 and 3 are sealed with a balloon to infer the content spillage.
By measuring the balloon's inflation amount before and after the grasp, the content is considered spilled if the inflation difference is larger than a threshold.

We use the balanced accuracy score to evaluate the prediction accuracy, which is the raw accuracy balanced by the number of successful and failed grasps in the collected data.
Table \ref{tab:phys_res} shows the balanced accuracy of the three grasp quality metrics for the two manipulation tasks and Fig. \ref{fig:phys_exp} shows examples of correct and wrong prediction of the planned minimal work grasps.
The proposed metric reaches 74.2\% and 71.3\% balanced accuracy for the two tasks. respectively, and are up to 24.2\% higher than the grasp reliability and the minimal force metric.
However, we note that the prediction accuracy is relatively low for object 5 (a plastic cup) compared to other objects.
This suggests the proposed algorithm for minimal work computation may not perform well for objects with large deformations.
Furthermore, the possible minimum grasp force with the Robotiq Gripper is much larger than the planned force and causes large object deformations and false positives.

\subsection{Limitations}
We note that the proposed method may not perform well for objects that having large deformations.
An Finite Element Method can be used to compute the deformation of such objects and the pressure distribution of the contact.
The grasp quality can then be computed based on objects's deformed shape.
Furthermore, one reason that leads to false positives is that the actual possible minimal grasp force of the Robotiq gripper is higher than specified in the planned grasps.
We plan to use e.g. a Schunk gripper mounted with force sensors to plan grasps with computed minimal grasp force.

\subsection{Future work}

We note that the minimal work grasp is applicable to grasping rigid objects, when the jaw pads are deformable.
We intend to further investigate this duality and plan grasps for both rigid and deformable objects.

redpajama_set_name: RedPajamaArXiv