/*! Initialize the stress calculation by setting initial block slip and stresses then calculating the stress in the whole system. */ void UpdateBlockStress::init(SimFramework *_sim) { BlockList::iterator nt; BlockID gid; int lid; double stress_drop, norm_velocity; sim = static_cast<Simulation *>(_sim); tmpBuffer = new double[sim->numGlobalBlocks()]; // All processes need the friction values for all blocks, so we set rhogd here // and transfer stress drop values between nodes later for (lid=0; lid<sim->numLocalBlocks(); ++lid) { double rho = 5.515e3; // density of rock in kg m^-3 double g = 9.81; // force of gravity in m s^-2 double depth = -sim->getBlock(gid).center()[2]; // depth of block center in m gid = sim->getGlobalBID(lid); sim->setRhogd(gid, rho*g*depth); // kg m^-3 * m s^-2 * m = kg m^-1 * s^-2 = Pa sim->setDynamicVal(gid, sim->getDynamic()); sim->setFailed(gid, false); // Set stresses to their specified initial values sim->setShearStress(gid, sim->getInitShearStress(gid)); sim->setNormalStress(gid, sim->getInitNormalStress(gid)); // Set the stress drop based on the Greens function calculations stress_drop = 0; norm_velocity = sim->getBlock(gid).slip_rate(); for (nt=sim->begin(); nt!=sim->end(); ++nt) { stress_drop += (nt->slip_rate()/norm_velocity)*sim->getGreenShear(gid, nt->getBlockID()); } sim->setStressDrop(gid, sim->getBlock(gid).max_slip()*stress_drop); // noise in the range [1-a, 1+a) sim->setSlipDeficit(gid, 0); if (sim->isLocalBlockID(gid)) { sim->decompressNormalRow(gid); //sim->setGreenNormal(bid, bid, 0.0); // Erase diagonal for normal Greens matrix sim->compressNormalRow(gid, 0.7); } } #ifdef MPI_C_FOUND // Transfer stress drop values between nodes // This is needed for normal stress Green's calculations for (gid=0; gid<sim->numGlobalBlocks(); ++gid) { double stress_drop; if (sim->isLocalBlockID(gid)) { stress_drop = sim->getStressDrop(gid); } MPI_Bcast(&stress_drop, 1, MPI_DOUBLE, sim->getBlockNode(gid), MPI_COMM_WORLD); if (!sim->isLocalBlockID(gid)) { sim->setStressDrop(gid, stress_drop); } } #endif // Compute initial stress on all blocks stressRecompute(); }
/*! Determine the next time step in the simulation when a failure occurs. Return the block ID of the block responsible for the failure and the timestep until the failure. */ void UpdateBlockStress::nextTimeStep(BlockVal &fail_time) { BlockList::iterator it; BlockVal temp_block_fail; double ts; VCEvent new_event; BlockID gid; int lid; quakelib::Conversion convert; // Set up the temporary buffer and update field for(it=sim->begin();it!=sim->end();++it) { tmpBuffer[it->getBlockID()] = 0.0; // Set the update field to be the slip rate of each block sim->setUpdateField(it->getBlockID(), it->slip_rate()); } // update the temporary buffer with the Greens function applied to the block slip rates sim->matrixVectorMultiplyAccum(tmpBuffer, sim->greenShear(), sim->getUpdateFieldPtr(), true); if (sim->doNormalStress()) { for(it=sim->begin();it!=sim->end();++it) { sim->setUpdateField(it->getBlockID(), -it->friction()*it->slip_rate()); } sim->matrixVectorMultiplyAccum(tmpBuffer, sim->greenNormal(), sim->getUpdateFieldPtr(), true); } // Go through the blocks and find which one will fail first temp_block_fail.val = DBL_MAX; temp_block_fail.block_id = UNDEFINED_BLOCK_ID; for(lid=0;lid<sim->numLocalBlocks();++lid) { gid = sim->getGlobalBID(lid); Block &block = sim->getBlock(gid); // Calculate the time until this block will fail // If the block has aseismic slip, the calculation is the exact solution of the // differential equation d(cff)/dt = rate_of_stress_change + cff*aseismic_frac*self_shear/recurrence if (block.aseismic() > 0) { double A, B, K; A = -tmpBuffer[gid]; B = -block.aseismic()*block.getSelfStresses()/block.getRecurrence(); K = -log(A+B*block.getCFF())/B; ts = K + log(A)/B; } else { ts = convert.sec2year(block.getCFF()/tmpBuffer[gid]); } // Blocks with negative timesteps are skipped. These effectively mean the block // should have failed already but didn't. This can happen in the starting phase // of a simulation due to initial stresses. These blocks will fail anyway in the // event propagation section, so we can safely ignore them here. However, negative // timesteps should not happen after the simulation has progressed for a while. if (ts <= 0) continue; // If the time to slip is less than the current shortest time, record the block if(ts < temp_block_fail.val) { temp_block_fail.block_id = gid; temp_block_fail.val = ts; } } // Each node now has the time before the first failure among its blocks // Determine the time to first failure over all nodes sim->allReduceBlockVal(temp_block_fail, fail_time, BLOCK_VAL_MIN); // If we didn't find any blocks that slipped, abort the simulation assertThrow(fail_time.val < DBL_MAX, "System stuck, no blocks to move."); // Setup the current event to have the trigger block ID and time new_event.setEventTriggerOnThisNode(fail_time.block_id==temp_block_fail.block_id); new_event.setEventTrigger(fail_time.block_id); new_event.setEventYear(sim->getYear()+fail_time.val); new_event.setEventNumber(sim->getEventCount()); sim->addEvent(new_event); }
/*! Determine the next time step in the simulation when a failure occurs. Return the block ID of the block responsible for the failure and the timestep until the failure. */ void UpdateBlockStress::nextStaticFailure(BlockVal &next_static_fail) { BlockList::iterator it; double ts; BlockID gid; int lid; quakelib::Conversion convert; // Set up the temporary buffer and update field for (it=sim->begin(); it!=sim->end(); ++it) { tmpBuffer[it->getBlockID()] = 0.0; // Set the update field to be the slip rate of each block sim->setUpdateField(it->getBlockID(), it->slip_rate()); } // update the temporary buffer with the Greens function applied to the block slip rates sim->matrixVectorMultiplyAccum(tmpBuffer, sim->greenShear(), sim->getUpdateFieldPtr(), true); if (sim->doNormalStress()) { for (it=sim->begin(); it!=sim->end(); ++it) { BlockID gid = it->getBlockID(); sim->setUpdateField(gid, -sim->getFriction(gid)*it->slip_rate()); } sim->matrixVectorMultiplyAccum(tmpBuffer, sim->greenNormal(), sim->getUpdateFieldPtr(), true); } // Go through the blocks and find which one will fail first next_static_fail.val = DBL_MAX; next_static_fail.block_id = UNDEFINED_ELEMENT_ID; for (lid=0; lid<sim->numLocalBlocks(); ++lid) { gid = sim->getGlobalBID(lid); Block &block = sim->getBlock(gid); // Calculate the time until this block will fail // If the block has aseismic slip, the calculation is the exact solution of the // differential equation d(cff)/dt = rate_of_stress_change + cff*aseismic_frac*self_shear/recurrence if (block.aseismic() > 0) { double A, B, K; A = -tmpBuffer[gid]; B = -block.aseismic()*sim->getSelfStresses(gid)/sim->getRecurrence(gid); K = -log(A+B*sim->getCFF(gid))/B; ts = K + log(A)/B; } else { ts = convert.sec2year(sim->getCFF(gid)/tmpBuffer[gid]); } // Blocks with negative timesteps are skipped. These effectively mean the block // should have failed already but didn't. This can happen in the starting phase // of a simulation due to initial stresses. These blocks will fail anyway in the // event propagation section, so we can safely ignore them here. However, negative // timesteps should not happen after the simulation has progressed for a while. if (ts <= 0) continue; // If the time to slip is less than the current shortest time, record the block // To ensure reproducibility with multiple processes, if multiple blocks fail // at the same time then we choose the block with the lowest ID over all the processes if (ts < next_static_fail.val) { next_static_fail.block_id = gid; next_static_fail.val = ts; } else if (ts == next_static_fail.val) { next_static_fail.block_id = (gid < next_static_fail.block_id ? gid : next_static_fail.block_id); } } }