/*! Set up the event recorder by defining the fault boundaries (in terms of block IDs). */ void EventRecorder::init(SimFramework *_sim) { VCSimulation *sim = static_cast<VCSimulation*>(_sim); std::string file_name = sim->getEventsFile().c_str(); BlockList::const_iterator it; if (!sim->isRootNode()) return; eFile.open(file_name.c_str()); assertThrow(eFile, "Failed to open events file: " + file_name); eFile << std::fixed << std::setprecision(2); // SETTING FID BOUNDS, ID OF THE FIRST AND THE LAST BLOCK FaultID prev_fid = UNDEFINED_FAULT_ID; BlockID start_block_id = UNDEFINED_BLOCK_ID; for(it=sim->begin();it!=sim->end();++it) { if(it->getFaultID() != prev_fid) { if (start_block_id != UNDEFINED_BLOCK_ID) { faultBlockIDs.push_back(std::make_pair(start_block_id, it->getBlockID()-1)); } start_block_id = it->getBlockID(); prev_fid = it->getFaultID(); } } faultBlockIDs.push_back(std::make_pair(start_block_id, sim->numGlobalBlocks()-1)); assertThrow(start_block_id!=UNDEFINED_BLOCK_ID, "Could not find any blocks in simulation."); }
/*! Allocate and initialize the arrays needed for a VC simulation. These include the shear and normal Greens function matrices and stress value arrays. */ void VCSimData::setupArrays(const unsigned int &global_sys_size, const unsigned int &local_sys_size, const bool &compressed, const bool &transposed) { int i; deallocateArrays(); global_size = global_sys_size; // Make the local size a factor of 16 to allow SSE loop unrolling local_size = local_sys_size+(16-local_sys_size%16); if (compressed) { if (transposed) { green_shear = new quakelib::CompressedRowMatrixTranspose<GREEN_VAL>(local_size, global_size); green_normal = new quakelib::CompressedRowMatrixTranspose<GREEN_VAL>(local_size, global_size); } else { green_shear = new quakelib::CompressedRowMatrixStraight<GREEN_VAL>(local_size, global_size); green_normal = new quakelib::CompressedRowMatrixStraight<GREEN_VAL>(local_size, global_size); } } else { if (transposed) { green_shear = new quakelib::DenseStdTranspose<GREEN_VAL>(local_size, global_size); green_normal = new quakelib::DenseStdTranspose<GREEN_VAL>(local_size, global_size); } else { green_shear = new quakelib::DenseStdStraight<GREEN_VAL>(local_size, global_size); green_normal = new quakelib::DenseStdStraight<GREEN_VAL>(local_size, global_size); } } shear_stress = (double*)malloc(sizeof(double)*global_size); assertThrow(shear_stress, "Not enough memory to allocate shear stress array."); normal_stress = (double*)malloc(sizeof(double)*global_size); assertThrow(normal_stress, "Not enough memory to allocate normal stress array."); f_shear_stress = (double*)malloc(sizeof(double)*global_size); assertThrow(f_shear_stress, "Not enough memory to allocate shear stress array."); f_normal_stress = (double*)malloc(sizeof(double)*global_size); assertThrow(f_normal_stress, "Not enough memory to allocate normal stress array."); update_field = (double*)malloc(sizeof(double)*global_size); assertThrow(update_field, "Not enough memory to allocate update field array."); for (i=0;i<global_size;++i) { shear_stress[i] = normal_stress[i] = 0; f_shear_stress[i] = f_normal_stress[i] = 0; update_field[i] = 0; } }
// Read the Greens function values in from a specified file void GreensFuncFileParse::CalculateGreens(Simulation *sim) { #ifdef HDF5_FOUND HDF5GreensDataReader *greens_file_reader; BlockID gid; int i, j, num_global_blocks; double *in_shear_green, *in_normal_green; // Open the Greens data file and initialize arrays to read in Greens values if (sim->getGreensInputfile().empty()) { sim->errConsole() << "ERROR: Greens input file undefined. Quitting." << std::endl; exit(-1); } num_global_blocks = sim->numGlobalBlocks(); greens_file_reader = new HDF5GreensDataReader(sim->getGreensInputfile()); assertThrow(greens_file_reader->getGreensDim() == num_global_blocks, "Greens input file not same dimension as model."); in_shear_green = new double[num_global_blocks]; in_normal_green = new double[num_global_blocks]; // Read the Greens function shear and normal values for (i=0; i<sim->numLocalBlocks(); ++i) { progressBar(sim, 0, i); gid = sim->getGlobalBID(i); greens_file_reader->getGreensVals(gid, in_shear_green, in_normal_green); for (j=0; j<num_global_blocks; ++j) { //// Schultz, excluding zero slip rate elements from sim by setting Greens to zero if (sim->getBlock(gid).slip_rate()==0 || sim->getBlock(j).slip_rate()==0) { sim->setGreens(gid, j, 0, 0); } else { sim->setGreens(gid, j, in_shear_green[j], in_normal_green[j]); } } } // use delete [] for c-arrays. delete greens_file_reader; delete [] in_shear_green; delete [] in_normal_green; #else assertThrow(false, "HDF5 is required to use Greens function file I/O."); #endif }
/*! Calculate the stress on all blocks and determine which block will be the next to fail and when it will fail. Use this to determine the slipDeficit on all blocks at the time of failure. Finally, use the new slipDeficit to recalculate the stress on all blocks at the time of failure. */ SimRequest UpdateBlockStress::run(SimFramework *_sim) { // Put a stress load on all blocks and determine which block will fail first int lid; BlockVal next_static_fail, next_aftershock, next_event, next_event_global; quakelib::Conversion convert; quakelib::ModelEvent new_event; // Calculate the current rates of stress change on all blocks stressRecompute(); // Given the rates of change, determine which block will fail next nextStaticFailure(next_static_fail); // Get the next aftershock event time nextAftershock(next_aftershock); // Take whichever is sooner, with ties going in favor of aftershocks if (next_static_fail.val < next_aftershock.val) { next_event.val = next_static_fail.val; next_event.block_id = next_static_fail.block_id; } else { next_event.val = next_aftershock.val; next_event.block_id = next_aftershock.block_id; } // Each node now has the time before the first failure among its blocks // Determine the time to first failure over all nodes sim->allReduceBlockVal(next_event, next_event_global, BLOCK_VAL_MIN); // If we didn't find any static failures or aftershocks, abort the simulation if (sim->isRootNode()) assertThrow(next_event_global.val < DBL_MAX, "System stuck, no blocks to move."); // Increment the simulation year to the next failure time and // update the slip on all other blocks sim->incrementYear(next_event_global.val); for (lid=0; lid<sim->numLocalBlocks(); ++lid) { BlockID gid = sim->getGlobalBID(lid); Block &local_block = sim->getBlock(gid); double cur_slip_deficit = sim->getSlipDeficit(gid); sim->setSlipDeficit(gid, cur_slip_deficit-local_block.slip_rate()*convert.year2sec(next_event_global.val)*(1.0-local_block.aseismic())); } // Recalculate the stress on all blocks with the new slip deficits stressRecompute(); // Record the current event new_event.setEventTriggerOnThisNode(next_event_global.block_id==next_static_fail.block_id); new_event.setEventTrigger(next_event_global.block_id); new_event.setEventYear(sim->getYear()); new_event.setEventNumber(sim->getEventCount()); sim->addEvent(new_event); if (sim->getYear() > sim->getSimDuration()) return SIM_STOP_REQUIRED; else return SIM_CONTINUE; }
void GreensFuncCalcBarnesHut::bhInnerCalc(Simulation *sim, quakelib::Octree<3> *tree, const BlockID &bid) { Block &source_block = sim->getBlock(bid); BlockIDList target_blocks; BlockIDList::const_iterator bit; BlockList::const_iterator it; double stress_values[2]; quakelib::Vec<3> target_point; quakelib::Octree<3> *local_node; quakelib::RectBound<3> local_bound; BlockID start_id, end_id; std::set<std::pair<BlockID, BlockID> >::const_iterator sbit; unsigned int i; quakelib::Vec<3> mean_center, mean_normal, rake_vec, up_vec, v0, v1, v2, v3; double mean_dip, mean_rake, mean_area, side_length; double mean_lambda, mean_mu, mean_unit_slip; Block repr_block; // Allocate row for this block (if using compressible matrices) sim->allocateShearNormalRows(bid); target_point = sim->getBlock(bid).center(); local_node = tree->get_leaf_containing_point(target_point); local_bound = local_node->bound(); quakelib::BHAnalyzer<3> bh_analysis(local_bound, sim->getBarnesHutTheta()); tree->traverse(&bh_analysis); // Ensure that we got all the fault segments assertThrow(bh_analysis.num_checks()==sim->numGlobalBlocks(), "Didn't find all necessary points in the octree."); sbit = bh_analysis.run_bounds.begin(); for (; sbit!=bh_analysis.run_bounds.end(); ++sbit) { start_id = sbit->first; end_id = sbit->second; // Make a list of target blocks target_blocks.clear(); for (i=start_id; i!=end_id; ++i) target_blocks.push_back(i); // Reset mean values/vectors mean_center = rake_vec = mean_normal = quakelib::Vec<3>(); mean_dip = mean_rake = mean_area = mean_lambda = mean_mu = mean_unit_slip = 0; // Convert the strains to Greens values and record them for (bit=target_blocks.begin(); bit!=target_blocks.end(); ++bit) { Block target_block = sim->getBlock(*bit); mean_center += target_block.center(); rake_vec += target_block.rake_vector(); mean_rake += target_block.rake(); mean_area += target_block.area(); mean_normal += target_block.normal(); mean_lambda += target_block.lame_lambda(); mean_mu += target_block.lame_mu(); mean_unit_slip += target_block.slip_rate(); } mean_center /= target_blocks.size(); rake_vec /= target_blocks.size(); mean_rake /= target_blocks.size(); mean_area /= target_blocks.size(); mean_lambda /= target_blocks.size(); mean_mu /= target_blocks.size(); mean_unit_slip /= target_blocks.size(); mean_normal /= target_blocks.size(); side_length = sqrt(mean_area); up_vec = -mean_normal.cross(rake_vec); v0 = mean_center + rake_vec*(side_length/2) + up_vec*(side_length/2); v1 = mean_center + rake_vec*(side_length/2) - up_vec*(side_length/2); v2 = mean_center - rake_vec*(side_length/2) - up_vec*(side_length/2); v3 = mean_center - rake_vec*(side_length/2) + up_vec*(side_length/2); repr_block.set_rake(mean_rake); repr_block.set_vert(0, v0); repr_block.set_vert(1, v1); repr_block.set_vert(2, v2); repr_block.set_vert(3, v3); repr_block.set_lame_lambda(mean_lambda); repr_block.set_lame_mu(mean_mu); repr_block.set_slip_rate(mean_unit_slip); repr_block.get_rake_and_normal_stress_due_to_block(stress_values, sim->getGreensSampleDistance(), source_block); // Set Green's values to averaged values for (bit=target_blocks.begin(); bit!=target_blocks.end(); ++bit) { sim->setGreens(*bit, bid, stress_values[0], stress_values[1]); } } // Compress the computed matrix row if savings are greater than 30% sim->compressShearRow(bid, 0.7); sim->compressNormalRow(bid, 0.7); }
void GreensFuncCalc::symmetrizeMatrix(Simulation *sim, GreensValsSparseMatrix &ssh) { double sxrl, sxru; int ir, ic, n; // If we're using MPI, exchange Greens values between nodes // in order to symmetrize the shear stress matrix #ifdef MPI_C_FOUND int i, world_size, local_rank, root_node, num_local_blocks, num_global_blocks; int *local_counts, *local_ids, *global_ids, *displs; GREEN_VAL *send_buf, *recv_buf; MPI_Datatype data_type; // MPI and decomposition values world_size = sim->getWorldSize(); local_rank = sim->getNodeRank(); // Get the size of the data we will transmit if (sizeof(GREEN_VAL)==4) data_type = MPI_FLOAT; else if (sizeof(GREEN_VAL)==8) data_type = MPI_DOUBLE; // Find out how many local objects each node has local_counts = new int[world_size]; num_local_blocks = sim->numLocalBlocks(); num_global_blocks = sim->numGlobalBlocks(); MPI_Allgather(&num_local_blocks, 1, MPI_INT, local_counts, 1, MPI_INT, MPI_COMM_WORLD); // Setup array for displacements displs = new int[world_size]; displs[0] = 0; for (n=1; n<world_size; ++n) displs[n] = displs[n-1]+local_counts[n-1]; // Find out the global block IDs for each local block on other nodes local_ids = new int[num_local_blocks]; for (n=0; n<num_local_blocks; ++n) local_ids[n] = sim->getGlobalBID(n); global_ids = new int[num_global_blocks]; MPI_Allgatherv(local_ids, num_local_blocks, MPI_INT, global_ids, local_counts, displs, MPI_INT, MPI_COMM_WORLD); // Setup send and receive buffers send_buf = new GREEN_VAL[num_global_blocks]; recv_buf = new GREEN_VAL[num_local_blocks]; // Go through each of the Greens function rows // If we computed the row, send pieces of it to other processors // If we need the row for symmetrization, receives pieces of it from another processor for (i=0; i<num_global_blocks; ++i) { root_node = sim->getBlockNode(i); // Fill the send buffer with the appropriate values if (root_node == local_rank) { for (n=0; n<num_global_blocks; ++n) { send_buf[n] = ssh[i][global_ids[n]]; } } // Scatter each of the required matrix sections // There is no need to send snorm since it isn't used outside the local node MPI_Scatterv(send_buf, local_counts, displs, data_type, recv_buf, num_local_blocks, data_type, root_node, MPI_COMM_WORLD); // Copy the receive buffer to the matrix for non-local transfers for (n=0; n<num_local_blocks; ++n) { if (root_node != local_rank) { ssh[i][n] = recv_buf[n]; } } } // use delete [] for c-arrays. delete [] local_counts; delete [] displs; delete [] local_ids; delete [] global_ids; delete [] send_buf; delete [] recv_buf; #endif // Symmetrize the shear stress matrices for (ir=0; ir<sim->numGlobalBlocks(); ++ir) { bool full_row = sim->isLocalBlockID(ir); int num_elems = (full_row ? sim->numGlobalBlocks() : sim->numLocalBlocks()); for (n=0; n<num_elems; ++n) { ic = (full_row ? n : sim->getGlobalBID(n)); double ir_area, ic_area; ir_area = sim->getBlock(ir).area(); ic_area = sim->getBlock(ic).area(); assertThrow(ir_area > 0 && ic_area > 0, "Blocks cannot have negative area."); int local_ir = (sim->isLocalBlockID(ic) ? ir : sim->getLocalInd(ir)); sxru=ssh[ir][n]*ic_area; sxrl=ssh[ic][local_ir]*ir_area; ssh[ir][n]=0.5*(sxrl + sxru)/ic_area; ssh[ic][local_ir]=0.5*(sxrl + sxru)/ir_area; } } }
/*! 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); }
int main (int argc, char **argv) { // Initialize the world (where the squares live), squares and vertices tsunamisquares::World this_world; tsunamisquares::SquareIDSet::const_iterator it; tsunamisquares::SquareIDSet ids; std::ifstream param_file; std::ofstream out_file; clock_t start,end; // -------------------------------------------------------------------------------- // /////////// CONSTANTS //////////// // -------------------------------------------------------------------------------- // /* Wilson: Trying a simple input file for these parameters. Can be improved in the future for readability and suseptability to errors const std::string out_file_name = "tsunami_output.txt"; // name this relevant const std::string bathy_file = "bathymetry/Pacific_900.txt"; // name this relevant //const std::string kml_file = "local/Pacific_36.kml"; // const std::string deformation_file = "local/Channel_Islands_test_bump.txt"; // kasey says we dont need this now // until later // Diffusion constant (fit to a reasonable looking sim) double D = 140616.45; //140616.45; // Flattening the bathymetry to a constant depth (negative for below sea level) double new_depth = -100.0; // Bumping up the bottom double bump_height = 50.0; // Number of times to move squares int N_steps = 20; //number of time steps 10 is fine, to see a bit of movement // because boundaries aren't defined very well, we limit the time steps whenever the water hits the walls of things // Updating intervals, etc. int current_step = 0; int update_step = 1; int save_step = 1; double time = 0.0; int output_num_digits_for_percent = 3; */ // Ensure we are given the parameter file name assertThrow(argc == 2, "usage: param_file"); param_file.open(argv[argc-1]); std::string param_name; std::string value; std::vector<std::string> param_values; while ( param_file >> param_name >> value ) { param_values.push_back(value); } const std::string out_file_name = param_values[0]; const std::string bathy_file = param_values[1]; const std::string kml_file = param_values[2]; const std::string deformation_file = param_values[3]; // Diffusion constant (fit to a reasonable looking sim) double D = atof(param_values[4].c_str()); //140616.45; // Time step in seconds double dt_param = atof(param_values[5].c_str()); // Number of times to move squares int N_steps = atof(param_values[6].c_str()); //number of time steps 10 is fine, to see a bit of movement // because boundaries aren't defined very well, we limit the time steps whenever the water hits the walls of things // Updating intervals, etc. int current_step = atof(param_values[7].c_str()); int update_step = atof(param_values[8].c_str()); int save_step = atof(param_values[9].c_str()); double time = atof(param_values[10].c_str()); int output_num_digits_for_percent = atof(param_values[11].c_str()); // Flattening the bathymetry to a constant depth (negative for below sea level) double flat_depth = atof(param_values[12].c_str()); // Bumping up the bottom double bump_height = atof(param_values[13].c_str()); //Boolean to decide whether to flatten the seafloor before running, for testing purposes bool flatten_bool = atof(param_values[14].c_str()); // Header for the simulation output const std::string header = "# time \t lon \t\t lat \t\t water height \t altitude \n"; // -------------------------------------------------------------------------------- // /////// Simulation Initialization and Loading /////// // --------------------------------------------------------------------------------// start = clock(); // Read in the bathymetry data this_world.clear(); std::cout << std::endl << "Reading..." << bathy_file.c_str() << std::endl; this_world.read_bathymetry(bathy_file.c_str()); // Index the neighbors by left/right/top etc. std::cout << "Indexing neighbors......" << std::endl; this_world.computeNeighbors(); // Compute the time step given the diffusion constant D //double dt = (double) (int) this_world.square(0).Lx()*this_world.square(0).Ly()/(2*D); //seconds // Use file-provided time step. double dt = dt_param; // Gather model information this_world.info(); int num_lats = this_world.num_lats(); int num_lons = this_world.num_lons(); std::cout << "Lons by Lats = (" << num_lons << ", " << num_lats << ")..."; ids = this_world.getSquareIDs(); double max_time = N_steps*dt; //tsunamisquares::SquareIDSet valids = this_world.square(0).get_valid_neighbors(); //tsunamisquares::SquareIDSet::const_iterator vit; //for (vit=valids.begin(); vit!=valids.end(); ++vit) { // std::cout << *vit << std::endl; //} // Write KML model //std::cout << "Writing KML..." << kml_file.c_str() << " ..."; //this_world.write_file_kml(kml_file.c_str()); // Flatten the bottom for simple simulation test cases, do not do this for tsunami simulations if(flatten_bool){ std::cout << "Flattening the bottom..."<< std::endl; this_world.flattenBottom(flat_depth); } // for (int pixel = 0; pixel++; pixel < (int) (num_lons*0.75)){ // tsunamisquares::UIndex landcenter = (int) (pixel); // tsunamisquares::UIndex landright = this_world.square(landcentral).right(); // this_world.deformBottom(landright, bump_height); // } // Put water into squares to bring water level up to sealevel. //std::cout << "Filling with water..." << std::flush; //this_world.fillToSeaLevel(); // for (int pixel_lons = 0; pixel_lons++; pixel_lons < num_lons){ // for ( int pixel_lats = 0; pixel_lats++; pixel_lats < ((int) num_lats*0.5) ) // this_world.deformBottom(pixel_lons*pixel_lats, bump_height*10); // } // Put water into squares to bring water level up to sealevel. std::cout << "Filling with water..." << std::endl; this_world.fillToSeaLevel(); // --------------------------------------------------------------------------------// // Sea Floor Deformation and Initial Conditions // // --------------------------------------------------------------------------------// std::cout << "Deforming the bottom... " << std::endl; // == DEFORM FROM FILE == std::cout << "\t Deforming from file" << std::endl; this_world.deformFromFile(deformation_file); //this_world.diffuseSquares(dt); // --------------------------------------------------------------------------------// // --== File I/O Preparation --== // // --------------------------------------------------------------------------------// out_file.open(out_file_name.c_str()); out_file << header.c_str(); std::cout.precision(output_num_digits_for_percent); // --------------------------------------------------------------------------------// // --========- Begin the Simulation; Move the Squares ----====- // // --------------------------------------------------------------------------------// std::cout << "Moving squares....time_step=" <<dt << "..."; while (time < max_time) { // If this is a writing step, print status if (current_step%update_step == 0) { std::cout << ".." << (100.0*current_step)/N_steps << "%.."; std::cout << std::flush; } // Write the current state to file if (current_step%save_step == 0) { for (it=ids.begin(); it!=ids.end(); ++it) { this_world.write_square_ascii(out_file, time, *it); } } // Move the squares this_world.moveSquares(dt); //TODO: turn back on: this_world.diffuseSquares(dt); //smoothing operation time += dt; current_step += 1; } out_file.close(); // --------------------------------------------------------------------------------// // --========--- Wrap up and Reporting ---=======--- // // --------------------------------------------------------------------------------// std::cout << std::endl << "Results written to " << out_file_name << std::endl; end = clock(); std::cout.precision(2+output_num_digits_for_percent); std::cout << "Total time: " << (float(end)-float(start))/CLOCKS_PER_SEC << " secs." << std::endl << std::endl; return 0; }